Skip to content

Commit dd63885

Browse files
authored
Fix Add artifact and build retention export and rules #88 (#89)
* Fix Add artifact and build retention export and rules #88 * Add configuration settings for retention rules
1 parent 607a8be commit dd63885

9 files changed

Lines changed: 353 additions & 2 deletions

example/best-practice/ps-rule.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ execution:
55
suppressionGroupExpired: Error
66

77
configuration:
8+
ArtifactMinimumRetentionDays: 7
9+
PullRequestRunsMinimumRetentionDays: 7
810
ProjectAdminsMinMembers: 2
911
ProjectAdminsMaxMembers: 4
1012
releaseMinimumProductionApproverCount: 1
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<#
2+
.SYNOPSIS
3+
Get retention settings for a project.
4+
5+
.DESCRIPTION
6+
Get retention settings for a project from Azure DevOps REST API.
7+
8+
.PARAMETER Project
9+
The project to get retention settings for.
10+
11+
.EXAMPLE
12+
Get-AzDevOpsRetentionSettings -Project 'MyProject'
13+
14+
.NOTES
15+
This function requires a connection to Azure DevOps. See Connect-AzDevOps for more information.
16+
#>
17+
Function Get-AzDevOpsRetentionSettings {
18+
[CmdletBinding()]
19+
param (
20+
[Parameter(Mandatory=$true)]
21+
[string]
22+
$Project
23+
)
24+
if($null -eq $script:connection) {
25+
throw 'Not connected to Azure DevOps. Run Connect-AzDevOps first.'
26+
}
27+
$Organization = $script:connection.Organization
28+
$header = $script:connection.GetHeader()
29+
30+
# Azure DevOps REST API endpoint for project retention settings
31+
$settingsUri = "https://dev.azure.com/$($Organization)/$($Project)/_apis/build/retention?api-version=7.1-preview.1"
32+
$policyUri = "https://dev.azure.com/$($Organization)/$($Project)/_apis/build/settings?api-version=7.1-preview.1"
33+
try {
34+
$settingsResponse = Invoke-RestMethod -Uri $settingsUri -Method Get -Headers $header
35+
$policyResponse = Invoke-RestMethod -Uri $policyUri -Method Get -Headers $header
36+
If($settingsResponse -is [string] -or $policyResponse -is [string]) {
37+
throw "Failed to get retention settings for project '$($Project)' from Azure DevOps"
38+
}
39+
}
40+
catch {
41+
throw "Failed to get retention settings for project '$($Project)' from Azure DevOps"
42+
}
43+
return @{
44+
RetentionSettings = $settingsResponse
45+
RetentionPolicy = $policyResponse
46+
ObjectType = 'Azure.DevOps.RetentionSettings'
47+
ObjectName = "$Organization.$Project.RetentionSettings"
48+
}
49+
}
50+
Export-ModuleMember -Function Get-AzDevOpsRetentionSettings
51+
52+
<#
53+
.SYNOPSIS
54+
Export retention settings for a project to a JSON file.
55+
56+
.DESCRIPTION
57+
Export retention settings for a project to a JSON file from Azure DevOps REST API.
58+
59+
.PARAMETER Project
60+
The project to get retention settings for.
61+
62+
.PARAMETER OutputPath
63+
The path to export the retention settings to.
64+
65+
.EXAMPLE
66+
Get-AzDevOpsRetentionSettings -Project 'MyProject' -OutputPath 'C:\Temp\'
67+
68+
.NOTES
69+
This function requires a connection to Azure DevOps. See Connect-AzDevOps for more information.
70+
#>
71+
Function Export-AzDevOpsRetentionSettings {
72+
[CmdletBinding()]
73+
param (
74+
[Parameter(Mandatory=$true)]
75+
[string]
76+
$Project,
77+
78+
[Parameter(Mandatory=$true)]
79+
[string]
80+
$OutputPath
81+
)
82+
$settings = Get-AzDevOpsRetentionSettings -Project $Project
83+
$settings | ConvertTo-Json -Depth 100 | Out-File -FilePath "$OutputPath\$($Project).ret.ado.json"
84+
}
85+
Export-ModuleMember -Function Export-AzDevOpsRetentionSettings

src/PSRule.Rules.AzureDevOps/PSRule.Rules.AzureDevOps.psm1

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ Function Export-AzDevOpsRuleData {
5757
Export-AzDevOpsReleaseDefinitions -Project $Project -OutputPath $OutputPath
5858
Write-Verbose "Exporting groups"
5959
Export-AzDevOpsGroups -Project $Project -OutputPath $OutputPath
60+
Write-Verbose "Exporting retention settings"
61+
Export-AzDevOpsRetentionSettings -Project $Project -OutputPath $OutputPath
6062
}
6163
Export-ModuleMember -Function Export-AzDevOpsRuleData -Alias Export-AzDevOpsProjectRuleData
6264
# End of Function Export-AzDevOpsRuleData
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
---
2+
category: Microsoft Azure DevOps Retention Settings
3+
severity: Severe
4+
online version: https://github.com/cloudyspells/PSRule.Rules.AzureDevOps/blob/main/src/PSRule.Rules.AzureDevOps/en/Azure.DevOps.RetentionSettings.ArtifactMinimumRetentionDays.md
5+
---
6+
7+
# Azure.DevOps.RetentionSettings.ArtifactMinimumRetentionDays
8+
9+
## SYNOPSIS
10+
11+
Retention settings for artifacts should be configured to meet compliance
12+
requirements. For example, a retention policy of 30 days may be required for
13+
production environments.
14+
15+
## DESCRIPTION
16+
17+
Retention settings for artifacts should be configured to meet compliance
18+
requirements. For example, a retention policy of 30 days may be required for
19+
production environments.
20+
21+
This rule requires a minimum retention period of 7 days. The rule is configurable
22+
to allow a different minimum retention period with the `ArtifactMinimumRetentionDays`
23+
conifguration setting.
24+
25+
Mininum TokenType: `ReadOnly`
26+
27+
## RECOMMENDATION
28+
29+
Consider setting a minimum retention period of more than 7 days for artifacts.
30+
31+
## LINKS
32+
33+
- [Define approvals and checks](https://learn.microsoft.com/en-us/azure/devops/pipelines/process/approvals?view=azure-devops&tabs=check-pass)
34+
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
category: Microsoft Azure DevOps Retention Settings
3+
severity: Severe
4+
online version: https://github.com/cloudyspells/PSRule.Rules.AzureDevOps/blob/main/src/PSRule.Rules.AzureDevOps/en/Azure.DevOps.RetentionSettings.PullRequestRunsMinimumRetentionDays.md
5+
---
6+
7+
# Azure.DevOps.RetentionSettings.PullRequestRunsMinimumRetentionDays
8+
9+
## SYNOPSIS
10+
11+
Retention settings for rull request runs should be configured to meet compliance
12+
requirements such as 30 days for production environments.
13+
14+
## DESCRIPTION
15+
16+
Retention settings for rull request runs should be configured to meet compliance
17+
requirements such as 30 days for production environments.
18+
19+
This rule requires a minimum retention period of 7 days. The rule is configurable
20+
to allow a different minimum retention period with the
21+
`PullRequestRunsMinimumRetentionDays` conifguration setting.
22+
23+
Mininum TokenType: `ReadOnly`
24+
25+
## RECOMMENDATION
26+
27+
Consider setting a minimum retention period of more than 7 days for pull request runs.
28+
29+
## LINKS
30+
31+
- [Define approvals and checks](https://learn.microsoft.com/en-us/azure/devops/pipelines/process/approvals?view=azure-devops&tabs=check-pass)
32+
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# PSRule rule definitions for Azure DevOps Retention Settings
2+
3+
# Synopsis: Retention settings should allow for artifacts to be retained for a minimum of 7 days
4+
Rule 'Azure.DevOps.RetentionSettings.ArtifactMinimumRetentionDays' `
5+
-Ref 'ADO-RET-001' `
6+
-Type 'Azure.DevOps.RetentionSettings' `
7+
-Tag @{ release = 'GA'} `
8+
-Level Warning {
9+
# Description "Retention settings allow for artifacts to be retained for a minimum of 7 days"
10+
Reason "Retention settings allow for artifacts to be retained for at least 7 days"
11+
Recommend "Consider increasing the minimum retention days to 7 days"
12+
# Links "https://docs.microsoft.com/en-us/azure/devops/pipelines/policies/retention?view=azure-devops#minimum-retention-days"
13+
AllOf {
14+
$Assert.HasField($TargetObject, "RetentionSettings.purgeArtifacts.value", $true)
15+
$Assert.GreaterOrEqual($TargetObject, "RetentionSettings.purgeArtifacts.value", $Configuration.GetValueOrDefault('ArtifactMinimumRetentionDays', 7))
16+
}
17+
}
18+
19+
# Synopsis: Retention settings should allow pull request runs to be retained at least 7 days
20+
Rule 'Azure.DevOps.RetentionSettings.PullRequestRunsMinimumRetentionDays' `
21+
-Ref 'ADO-RET-002' `
22+
-Type 'Azure.DevOps.RetentionSettings' `
23+
-Tag @{ release = 'GA'} `
24+
-Level Warning {
25+
# Description "Retention settings should allow pull request runs to be retained at least 7 days"
26+
Reason "Retention settings should allow pull request runs to be retained at least 7 days"
27+
Recommend "Consider increasing the minimum retention days to 7 days"
28+
# Links "https://docs.microsoft.com/en-us/azure/devops/pipelines/policies/retention?view=azure-devops#minimum-retention-days"
29+
AllOf {
30+
$Assert.HasField($TargetObject, "RetentionSettings.purgePullRequestRuns.value", $true)
31+
$Assert.GreaterOrEqual($TargetObject, "RetentionSettings.purgePullRequestRuns.value", $Configuration.GetValueOrDefault('PullRequestRunsMinimumRetentionDays', 7))
32+
}
33+
}

tests/DevOps.Groups.Tests.ps1

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,3 +297,8 @@ Describe "Azure.DevOps.Group" {
297297
}
298298
}
299299
}
300+
301+
AfterAll {
302+
Disconnect-AzDevOps
303+
Remove-Module -Name PSRule.Rules.AzureDevOps -Force
304+
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
BeforeAll {
2+
$rootPath = $PWD;
3+
Import-Module -Name (Join-Path -Path $rootPath -ChildPath '/src/PSRule.Rules.AzureDevOps/PSRule.Rules.AzureDevOps.psd1') -Force;
4+
}
5+
6+
Describe 'Azure.DevOps.RetentionSettings' {
7+
Context ' Get-AzDevOpsRetentionSettings without a connection' {
8+
It 'should throw an error' {
9+
{
10+
Disconnect-AzDevOps
11+
Get-AzDevOpsRetentionSettings -Project 'MyProject'
12+
} | Should -Throw 'Not connected to Azure DevOps. Run Connect-AzDevOps first.'
13+
}
14+
}
15+
16+
Context ' Get-AzDevOpsRetentionSettings' {
17+
BeforeAll {
18+
Connect-AzDevOps -Organization $env:ADO_ORGANIZATION -PAT $env:ADO_PAT
19+
$response = Get-AzDevOpsRetentionSettings -Project $env:ADO_PROJECT
20+
}
21+
22+
It 'should return a hashtable' {
23+
$response | Should -BeOfType [hashtable]
24+
}
25+
26+
It 'should return a hashtable with RetentionSettings property' {
27+
$response.RetentionSettings | Should -Not -BeNullOrEmpty
28+
}
29+
30+
It 'should return a hashtable with RetentionPolicy property' {
31+
$response.RetentionPolicy | Should -Not -BeNullOrEmpty
32+
}
33+
34+
It 'should return a hashtable with ObjectType property' {
35+
$response.ObjectType | Should -Not -BeNullOrEmpty
36+
}
37+
38+
It 'should return a hashtable with ObjectName property' {
39+
$response.ObjectName | Should -Not -BeNullOrEmpty
40+
}
41+
42+
AfterAll {
43+
Disconnect-AzDevOps
44+
}
45+
}
46+
47+
Context ' Get-AzDevOpsRetentionSettings with a wrong organization or PAT' {
48+
It 'should throw an error with a wrong PAT' {
49+
{
50+
Disconnect-AzDevOps
51+
Connect-AzDevOps -Organization $env:ADO_ORGANIZATION -PAT 'wrongPAT'
52+
Get-AzDevOpsRetentionSettings -Project $env:ADO_PROJECT
53+
} | Should -Throw "Failed to get retention settings for project '$($env:ADO_PROJECT)' from Azure DevOps"
54+
}
55+
56+
It 'should throw an error with a wrong organization' {
57+
{
58+
Disconnect-AzDevOps
59+
Connect-AzDevOps -Organization 'wrongOrganization' -PAT $env:ADO_PAT
60+
Get-AzDevOpsRetentionSettings -Project $env:ADO_PROJECT
61+
} | Should -Throw "Failed to get retention settings for project '$($env:ADO_PROJECT)' from Azure DevOps"
62+
}
63+
64+
It 'should throw an error with a wrong project' {
65+
{
66+
Disconnect-AzDevOps
67+
Connect-AzDevOps -Organization $env:ADO_ORGANIZATION -PAT $env:ADO_PAT
68+
Get-AzDevOpsRetentionSettings -Project 'wrongProject'
69+
} | Should -Throw "Failed to get retention settings for project 'wrongProject' from Azure DevOps"
70+
}
71+
}
72+
73+
Context ' Export-AzDevOpsRetentionSettings without a connection' {
74+
It 'should throw an error' {
75+
{
76+
Disconnect-AzDevOps
77+
Export-AzDevOpsRetentionSettings -Project 'MyProject' -OutputPath $Env:ADO_EXPORT_DIR
78+
} | Should -Throw 'Not connected to Azure DevOps. Run Connect-AzDevOps first.'
79+
}
80+
}
81+
82+
Context ' Export-AzDevOpsRetentionSettings' {
83+
BeforeAll {
84+
Connect-AzDevOps -Organization $env:ADO_ORGANIZATION -PAT $env:ADO_PAT
85+
Export-AzDevOpsRetentionSettings -Project $env:ADO_PROJECT -OutputPath $Env:ADO_EXPORT_DIR
86+
}
87+
88+
It 'should export a JSON file' {
89+
$file = Get-ChildItem -Path $Env:ADO_EXPORT_DIR -Filter "$($env:ADO_PROJECT).ret.ado.json" -Recurse -ErrorAction SilentlyContinue | Select-Object -ExpandProperty 'FullName'
90+
$file | Should -Not -BeNullOrEmpty
91+
}
92+
93+
It 'should export a JSON file with the correct name' {
94+
$file = Get-ChildItem -Path $Env:ADO_EXPORT_DIR -Filter "$($env:ADO_PROJECT).ret.ado.json" -Recurse -ErrorAction SilentlyContinue | Select-Object -ExpandProperty 'FullName'
95+
$file | Should -Match "$($env:ADO_PROJECT).ret.ado.json"
96+
}
97+
98+
It 'should export a JSON file with the correct content' {
99+
$file = Get-ChildItem -Path $Env:ADO_EXPORT_DIR -Filter "$($env:ADO_PROJECT).ret.ado.json" -Recurse -ErrorAction SilentlyContinue | Select-Object -ExpandProperty 'FullName'
100+
$content = Get-Content -Path $file -Raw
101+
$content | Should -Match 'RetentionSettings'
102+
$content | Should -Match 'RetentionPolicy'
103+
$content | Should -Match 'ObjectType'
104+
$content | Should -Match 'ObjectName'
105+
}
106+
107+
AfterAll {
108+
Disconnect-AzDevOps
109+
}
110+
}
111+
112+
Context ' Export-AzDevOpsRetentionSettings with a wrong organization or PAT' {
113+
It 'should throw an error with a wrong PAT' {
114+
{
115+
Disconnect-AzDevOps
116+
Connect-AzDevOps -Organization $env:ADO_ORGANIZATION -PAT 'wrongPAT'
117+
Export-AzDevOpsRetentionSettings -Project $env:ADO_PROJECT -OutputPath $Env:ADO_EXPORT_DIR
118+
} | Should -Throw "Failed to get retention settings for project '$($env:ADO_PROJECT)' from Azure DevOps"
119+
}
120+
121+
It 'should throw an error with a wrong organization' {
122+
{
123+
Disconnect-AzDevOps
124+
Connect-AzDevOps -Organization 'wrongOrganization' -PAT $env:ADO_PAT
125+
Export-AzDevOpsRetentionSettings -Project $env:ADO_PROJECT -OutputPath $Env:ADO_EXPORT_DIR
126+
} | Should -Throw "Failed to get retention settings for project '$($env:ADO_PROJECT)' from Azure DevOps"
127+
}
128+
129+
It 'should throw an error with a wrong project' {
130+
{
131+
Disconnect-AzDevOps
132+
Connect-AzDevOps -Organization $env:ADO_ORGANIZATION -PAT $env:ADO_PAT
133+
Export-AzDevOpsRetentionSettings -Project 'wrongProject' -OutputPath $Env:ADO_EXPORT_DIR
134+
} | Should -Throw "Failed to get retention settings for project 'wrongProject' from Azure DevOps"
135+
}
136+
}
137+
}
138+
139+
AfterAll {
140+
Disconnect-AzDevOps
141+
Remove-Module -Name PSRule.Rules.AzureDevOps -Force
142+
}

tests/Rules.Tests.ps1

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@ BeforeAll {
4949

5050
Describe 'AzureDevOps ' {
5151
Context 'Base rules ' {
52-
It 'Should contain 57 rules' {
52+
It 'Should contain 59 rules' {
5353
$rules = Get-PSRule -Module PSRule.Rules.AzureDevOps;
54-
$rules.Count | Should -Be 57;
54+
$rules.Count | Should -Be 59;
5555
}
5656
}
5757

@@ -1688,4 +1688,20 @@ Describe 'AzureDevOps ' {
16881688
$ruleHits.Count | Should -Be 1;
16891689
}
16901690
}
1691+
1692+
Context 'Azure.DevOps.RetentionSettings.ArtifactMinimumRetentionDays' {
1693+
It 'Should pass once' {
1694+
$ruleHits = @($ruleResult | Where-Object { $_.RuleName -eq 'Azure.DevOps.RetentionSettings.ArtifactMinimumRetentionDays' })
1695+
$ruleHits[0].Outcome | Should -Be 'Pass';
1696+
$ruleHits.Count | Should -Be 1;
1697+
}
1698+
}
1699+
1700+
Context 'Azure.DevOps.RetentionSettings.PullRequestRunsMinimumRetentionDays' {
1701+
It 'Should pass once' {
1702+
$ruleHits = @($ruleResult | Where-Object { $_.RuleName -eq 'Azure.DevOps.RetentionSettings.PullRequestRunsMinimumRetentionDays' })
1703+
$ruleHits[0].Outcome | Should -Be 'Pass';
1704+
$ruleHits.Count | Should -Be 1;
1705+
}
1706+
}
16911707
}

0 commit comments

Comments
 (0)