diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 788d7de..8565036 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -31,7 +31,7 @@ jobs:
runs-on: ubuntu-latest
env:
DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
- IMAGE_NAME: 'azure-devops-cleaner'
+ IMAGE_NAME: 'azure-resources-cleaner'
DOCKER_BUILDKIT: 1 # Enable Docker BuildKit
steps:
@@ -62,9 +62,9 @@ jobs:
- name: Publish
run: |
dotnet publish \
- ${{ github.workspace }}/Tingle.AzdoCleaner/Tingle.AzdoCleaner.csproj \
+ ${{ github.workspace }}/Tingle.AzureCleaner/Tingle.AzureCleaner.csproj \
-c Release \
- -o ${{ github.workspace }}/drop/Tingle.AzdoCleaner
+ -o ${{ github.workspace }}/drop/Tingle.AzureCleaner
- name: Replace tokens
uses: cschleiden/replace-tokens@v1
@@ -109,11 +109,12 @@ jobs:
- name: Pull Docker base image & warm Docker cache
run: docker pull "ghcr.io/${{ github.repository_owner }}/${{ env.IMAGE_NAME }}:latest"
+ continue-on-error: true
- name: Build image
run: |
docker build \
- -f Tingle.AzdoCleaner/Dockerfile \
+ -f Tingle.AzureCleaner/Dockerfile \
--label com.github.image.run.id=${{ github.run_id }} \
--label com.github.image.run.number=${{ github.run_number }} \
--label com.github.image.job.id=${{ github.job }} \
diff --git a/.github/workflows/cleanup.yml b/.github/workflows/cleanup.yml
index c611ec4..35f0807 100644
--- a/.github/workflows/cleanup.yml
+++ b/.github/workflows/cleanup.yml
@@ -13,7 +13,7 @@ jobs:
- name: Delete old packages
uses: actions/delete-package-versions@v5
with:
- package-name: 'azure-devops-cleaner'
+ package-name: 'azure-resources-cleaner'
package-type: 'container'
min-versions-to-keep: 20
delete-only-pre-release-versions: "true"
diff --git a/README.md b/README.md
index f19fb60..c3de45f 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
# Azure DevOps Cleaner
-![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/tinglesoftware/azure-devops-cleaner/build.yml?branch=main&style=flat-square)
-[![Release](https://img.shields.io/github/release/tinglesoftware/azure-devops-cleaner.svg?style=flat-square)](https://github.com/tinglesoftware/azure-devops-cleaner/releases/latest)
-[![license](https://img.shields.io/github/license/tinglesoftware/azure-devops-cleaner.svg?style=flat-square)](LICENSE)
+![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/tinglesoftware/azure-resources-cleaner/build.yml?branch=main&style=flat-square)
+[![Release](https://img.shields.io/github/release/tinglesoftware/azure-resources-cleaner.svg?style=flat-square)](https://github.com/tinglesoftware/azure-resources-cleaner/releases/latest)
+[![license](https://img.shields.io/github/license/tinglesoftware/azure-resources-cleaner.svg?style=flat-square)](LICENSE)
This repository houses a convenience tool for cleaning up resources based on the terminal status of pull requests in Azure DevOps. This is particularly useful in removing the reviewApp resources in environments, that created automatically by Azure Pipelines. In addition, it will also cleanup resources deployed to Azure.
@@ -23,9 +23,9 @@ This repository houses a convenience tool for cleaning up resources based on the
### Deployment to Azure
-[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Ftinglesoftware%2Fazure-devops-cleaner%2Fmain%2Fmain.json)
-[![Deploy to Azure US Gov](https://aka.ms/deploytoazuregovbutton)](https://portal.azure.us/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Ftinglesoftware%2Fazure-devops-cleaner%2Fmain%2Fmain.json)
-[![Visualize](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/visualizebutton.svg?sanitize=true)](http://armviz.io/#/?load=https%3A%2F%2Fraw.githubusercontent.com%2Ftinglesoftware%2Fazure-devops-cleaner%2Fmain%2Fmain.json)
+[![Deploy to Azure](https://aka.ms/deploytoazurebutton)](https://portal.azure.com/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Ftinglesoftware%2Fazure-resources-cleaner%2Fmain%2Fmain.json)
+[![Deploy to Azure US Gov](https://aka.ms/deploytoazuregovbutton)](https://portal.azure.us/#create/Microsoft.Template/uri/https%3A%2F%2Fraw.githubusercontent.com%2Ftinglesoftware%2Fazure-resources-cleaner%2Fmain%2Fmain.json)
+[![Visualize](https://raw.githubusercontent.com/Azure/azure-quickstart-templates/master/1-CONTRIBUTION-GUIDE/images/visualizebutton.svg?sanitize=true)](http://armviz.io/#/?load=https%3A%2F%2Fraw.githubusercontent.com%2Ftinglesoftware%2Fazure-resources-cleaner%2Fmain%2Fmain.json)
The easiest means of deployment is to use the relevant button above. You can also use the [main.json](/main.json) or [main.bicep](/main.bicep) files. You will need an Azure subscription and a resource group to deploy to any of the Azure hosts.
@@ -35,7 +35,7 @@ The easiest means of deployment is to use the relevant button above. You can als
|azureDevOpsProjectUrl|The URL of the Azure DevOps project or collection. For example `https://dev.azure.com/fabrikam/DefaultCollection`. This URL must be accessible from the network that the deployment is done in. You can modify the deployment to be done in an private network but you are on your own there.|Yes|**none**|
|azureDevOpsProjectToken|Personal Access Token (PAT) for accessing the Azure DevOps project. It must have `Environment (Read & Manage)` permissions.|Yes|**none**|
|location|Location to deploy the resources.|No|<resource-group-location>|
-|name|The name of all resources.|No|`azdo-cleaner`|
+|name|The name of all resources.|No|`azure-cleaner`|
|dockerImageTag|The image tag to use when pulling the docker container. A tag also defines the version. You should avoid using `latest`. Example: `0.1.0`|No|<version-downloaded>|
> The template includes a User Assigned Managed Identity, which is used when performing Azure Resource Manager operations such as deletions. After deployment, you should assign `Contributor` permissions to it where you want it to operate such as a subscription or a resource group. See [official docs](https://learn.microsoft.com/en-us/azure/role-based-access-control/role-assignments-portal-managed-identity#user-assigned-managed-identity) for how to assign permissions.
You can also do the role assignment on a management group. The tool scans for subscriptions that it has access to before listing the resources of a given type so you need not change anything in the deployment after altering permissions.
@@ -63,7 +63,7 @@ If you use the [REST API](https://learn.microsoft.com/en-us/rest/api/azure/devop
"consumerActionId": "httpRequest",
"publisherInputs": {
"notificationType": "StatusUpdateNotification",
- "projectId": ""
+ "projectId": ""
},
"consumerInputs": {
"detailedMessagesToSend": "none",
@@ -75,7 +75,7 @@ If you use the [REST API](https://learn.microsoft.com/en-us/rest/api/azure/devop
}
```
-> When using Azure Container Apps, the url should have the format:
`https://azdo-cleaner.{envrionment-unique-dentifier}.{region}.azurecontainerapps.io/webhooks/azure`
For example: `https://azdo-cleaner.blackplant-123456a7.westeurope.azurecontainerapps.io/webhooks/azure`
+> When using Azure Container Apps, the url should have the format:
`https://azure-cleaner.{envrionment-unique-dentifier}.{region}.azurecontainerapps.io/webhooks/azure`
For example: `https://azure-cleaner.blackplant-123456a7.westeurope.azurecontainerapps.io/webhooks/azure`
## What is supported?
@@ -87,7 +87,7 @@ This tool looks for resources or sub-resources named in a number of formats:
- `ra-{pull-request-identifier}`
- `ra{pull-request-identifier}`
-For example: `ra-2215`, `ra2215`, and `review-app-2215` will all be handled. Make sure you name your preview environments accordingly. If you wish to contribute more reasonable patterns, check [here](https://github.com/tinglesoftware/azure-devops-cleaner/blob/7e21f338f78f6af634d8aa35d39542455c55415b/Tingle.AzdoCleaner/AzdoEventHandler.cs#L100)
+For example: `ra-2215`, `ra2215`, and `review-app-2215` will all be handled. Make sure you name your preview environments accordingly. If you wish to contribute more reasonable patterns, check [here](https://github.com/tinglesoftware/azure-resources-cleaner/blob/7e21f338f78f6af634d8aa35d39542455c55415b/Tingle.AzureCleaner/AzureCleaner.cs#L100)
### Preview environments on Azure DevOps
diff --git a/Tingle.AzdoCleaner.Tests/AzdoEventHandlerTests.cs b/Tingle.AzdoCleaner.Tests/AzdoEventHandlerTests.cs
deleted file mode 100644
index 84b134a..0000000
--- a/Tingle.AzdoCleaner.Tests/AzdoEventHandlerTests.cs
+++ /dev/null
@@ -1,123 +0,0 @@
-using Azure.Core;
-using Microsoft.Extensions.Caching.Memory;
-using Microsoft.Extensions.Logging;
-using Microsoft.Extensions.Options;
-using Xunit;
-
-namespace Tingle.AzdoCleaner.Tests;
-
-public class AzdoEventHandlerTests
-{
- [Fact]
- public void TryFindProject_Works()
- {
- var cache = new MemoryCache(Options.Create(new MemoryCacheOptions()));
- var optionsAccessor = Options.Create(new AzureDevOpsEventHandlerOptions
- {
- Projects =
- [
- "https://dev.azure.com/fabrikam/DefaultCollection;123456789",
- "https://dev.azure.com/fabrikam/cea8cb01-dd13-4588-b27a-55fa170e4e94;987654321",
- ],
- });
- var logger = new LoggerFactory().CreateLogger();
- var handler = new AzdoEventHandler(cache, optionsAccessor, logger);
-
- // not found
- Assert.False(handler.TryFindProject("https://dev.azure.com/fabrikam/6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c", out _, out _));
-
- // proper one
- Assert.True(handler.TryFindProject("https://dev.azure.com/fabrikam/DefaultCollection", out var url, out var token));
- Assert.Equal("https://dev.azure.com/fabrikam/DefaultCollection", url);
- Assert.Equal("123456789", token);
-
- // with project id
- Assert.True(handler.TryFindProject("https://dev.azure.com/fabrikam/_apis/projects/cea8cb01-dd13-4588-b27a-55fa170e4e94", out url, out token));
- Assert.Equal("https://dev.azure.com/fabrikam/cea8cb01-dd13-4588-b27a-55fa170e4e94", url);
- Assert.Equal("987654321", token);
-
- // with remote URL 1
- Assert.True(handler.TryFindProject("https://dev.azure.com/fabrikam/DefaultCollection/_git/Fabrikam", out url, out token));
- Assert.Equal("https://dev.azure.com/fabrikam/DefaultCollection", url);
- Assert.Equal("123456789", token);
-
- // with remote URL 2
- Assert.True(handler.TryFindProject("https://tingle@dev.azure.com/fabrikam/DefaultCollection/_git/Fabrikam", out url, out token));
- Assert.Equal("https://dev.azure.com/fabrikam/DefaultCollection", url);
- Assert.Equal("123456789", token);
- }
-
- [Fact]
- public void MakePossibleNames_Works()
- {
- Assert.Equal(["review-app-23765", "ra-23765", "ra23765"],
- AzdoEventHandler.MakePossibleNames([23765]));
- Assert.Equal(["review-app-23765", "ra-23765", "ra23765", "review-app-50", "ra-50", "ra50"],
- AzdoEventHandler.MakePossibleNames([23765, 50]));
- }
-
- [Fact]
- public void NameMatchesExpectedFormat_Works()
- {
- var possibleNames = AzdoEventHandler.MakePossibleNames([23765]);
-
- // works for all in exact format
- var modified = possibleNames;
- Assert.All(modified, pn => AzdoEventHandler.NameMatchesExpectedFormat(possibleNames, pn));
-
- // works when prefixed
- modified = possibleNames.Select(pn => $"bla:{pn}").ToList();
- Assert.All(modified, pn => AzdoEventHandler.NameMatchesExpectedFormat(possibleNames, pn));
-
- // works when suffixed
- modified = possibleNames.Select(pn => $"{pn}:bla").ToList();
- Assert.All(modified, pn => AzdoEventHandler.NameMatchesExpectedFormat(possibleNames, pn));
-
- // works for AppServicePlan
- var resourceId = new ResourceIdentifier($"/subscriptions/{Guid.Empty}/resourceGroups/FABRIKAM/providers/Microsoft.Web/serverfarms/fabrikam-sites-ra23765");
- Assert.True(AzdoEventHandler.NameMatchesExpectedFormat(possibleNames, resourceId));
-
- // works for ManagedEnvironment
- resourceId = new ResourceIdentifier($"/subscriptions/{Guid.Empty}/resourceGroups/FABRIKAM/providers/Microsoft.App/managedEnvironments/fabrikam-sites-ra-23765");
- Assert.True(AzdoEventHandler.NameMatchesExpectedFormat(possibleNames, resourceId));
-
- // works for Azure SQL Database
- resourceId = new ResourceIdentifier($"/subscriptions/{Guid.Empty}/resourceGroups/FABRIKAM/providers/Microsoft.Sql/servers/fabrikam/databases/fabrikam-sites-ra-23765");
- Assert.True(AzdoEventHandler.NameMatchesExpectedFormat(possibleNames, resourceId));
-
- // skips Azure SQL Database master database
- resourceId = new ResourceIdentifier($"/subscriptions/{Guid.Empty}/resourceGroups/FABRIKAM/providers/Microsoft.Sql/servers/fabrikam/databases/master");
- Assert.False(AzdoEventHandler.NameMatchesExpectedFormat(possibleNames, resourceId));
- }
-
- [Fact]
- public async Task HandleAsync_Works()
- {
- var cache = new MemoryCache(Options.Create(new MemoryCacheOptions()));
- var optionsAccessor = Options.Create(new AzureDevOpsEventHandlerOptions
- {
- Projects = ["https://dev.azure.com/fabrikam/DefaultCollection;123456789"],
- });
- var logger = new LoggerFactory().CreateLogger();
- var handler = new ModifiedAzdoEventHandler(cache, optionsAccessor, logger);
-
- await handler.HandleAsync(prId: 1,
- remoteUrl: "https://dev.azure.com/fabrikam/DefaultCollection/_git/Fabrikam",
- rawProjectUrl: "https://dev.azure.com/fabrikam/DefaultCollection/_apis/projects/6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c");
- var (url, token, prIds) = Assert.Single(handler.Calls);
- Assert.Equal("https://dev.azure.com/fabrikam/DefaultCollection", url);
- Assert.Equal("123456789", token);
- Assert.Equal([1], prIds);
- }
-
- class ModifiedAzdoEventHandler(IMemoryCache cache, IOptions options, ILogger logger) : AzdoEventHandler(cache, options, logger)
- {
- public List<(AzdoProjectUrl url, string? token, IEnumerable prIds)> Calls { get; } = [];
-
- protected override Task DeleteReviewAppResourcesAsync(AzdoProjectUrl url, string? token, IEnumerable prIds, CancellationToken cancellationToken = default)
- {
- Calls.Add((url, token, prIds));
- return Task.CompletedTask;
- }
- }
-}
diff --git a/Tingle.AzdoCleaner.sln b/Tingle.AzdoCleaner.sln
index 7bb9dd1..fda35d7 100644
--- a/Tingle.AzdoCleaner.sln
+++ b/Tingle.AzdoCleaner.sln
@@ -3,9 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.3.32901.215
MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.AzdoCleaner", "Tingle.AzdoCleaner\Tingle.AzdoCleaner.csproj", "{F3BDB109-999B-4F3C-A6C5-745CC5BE4B68}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.AzureCleaner", "Tingle.AzureCleaner\Tingle.AzureCleaner.csproj", "{F3BDB109-999B-4F3C-A6C5-745CC5BE4B68}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.AzdoCleaner.Tests", "Tingle.AzdoCleaner.Tests\Tingle.AzdoCleaner.Tests.csproj", "{BCDFC1A1-1FF4-4F26-9DEC-676CEC1418BF}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tingle.AzureCleaner.Tests", "Tingle.AzureCleaner.Tests\Tingle.AzureCleaner.Tests.csproj", "{BCDFC1A1-1FF4-4F26-9DEC-676CEC1418BF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/Tingle.AzdoCleaner.Tests/AzdoProjectUrlTests.cs b/Tingle.AzureCleaner.Tests/AzdoProjectUrlTests.cs
similarity index 96%
rename from Tingle.AzdoCleaner.Tests/AzdoProjectUrlTests.cs
rename to Tingle.AzureCleaner.Tests/AzdoProjectUrlTests.cs
index d9a18c3..1139ce8 100644
--- a/Tingle.AzdoCleaner.Tests/AzdoProjectUrlTests.cs
+++ b/Tingle.AzureCleaner.Tests/AzdoProjectUrlTests.cs
@@ -1,6 +1,6 @@
using Xunit;
-namespace Tingle.AzdoCleaner.Tests;
+namespace Tingle.AzureCleaner.Tests;
public class AzdoProjectUrlTests
{
diff --git a/Tingle.AzureCleaner.Tests/AzureCleanerTests.cs b/Tingle.AzureCleaner.Tests/AzureCleanerTests.cs
new file mode 100644
index 0000000..1c67341
--- /dev/null
+++ b/Tingle.AzureCleaner.Tests/AzureCleanerTests.cs
@@ -0,0 +1,132 @@
+using Azure.Core;
+using Microsoft.Extensions.Caching.Memory;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+using Xunit;
+
+namespace Tingle.AzureCleaner.Tests;
+
+public class AzureCleanerTests
+{
+ [Fact]
+ public void TryFindAzdoProject_Works()
+ {
+ var cache = new MemoryCache(Options.Create(new MemoryCacheOptions()));
+ var optionsAccessor = Options.Create(new AzureCleanerOptions
+ {
+ AzdoProjects =
+ [
+ "https://dev.azure.com/fabrikam/DefaultCollection;123456789",
+ "https://dev.azure.com/fabrikam/cea8cb01-dd13-4588-b27a-55fa170e4e94;987654321",
+ ],
+ });
+ var logger = new LoggerFactory().CreateLogger();
+ var cleaner = new AzureCleaner(cache, optionsAccessor, logger);
+
+ // not found
+ Assert.False(cleaner.TryFindAzdoProject("https://dev.azure.com/fabrikam/6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c", out _, out _));
+
+ // proper one
+ Assert.True(cleaner.TryFindAzdoProject("https://dev.azure.com/fabrikam/DefaultCollection", out var url, out var token));
+ Assert.Equal("https://dev.azure.com/fabrikam/DefaultCollection", url);
+ Assert.Equal("123456789", token);
+
+ // with project id
+ Assert.True(cleaner.TryFindAzdoProject("https://dev.azure.com/fabrikam/_apis/projects/cea8cb01-dd13-4588-b27a-55fa170e4e94", out url, out token));
+ Assert.Equal("https://dev.azure.com/fabrikam/cea8cb01-dd13-4588-b27a-55fa170e4e94", url);
+ Assert.Equal("987654321", token);
+
+ // with remote URL 1
+ Assert.True(cleaner.TryFindAzdoProject("https://dev.azure.com/fabrikam/DefaultCollection/_git/Fabrikam", out url, out token));
+ Assert.Equal("https://dev.azure.com/fabrikam/DefaultCollection", url);
+ Assert.Equal("123456789", token);
+
+ // with remote URL 2
+ Assert.True(cleaner.TryFindAzdoProject("https://tingle@dev.azure.com/fabrikam/DefaultCollection/_git/Fabrikam", out url, out token));
+ Assert.Equal("https://dev.azure.com/fabrikam/DefaultCollection", url);
+ Assert.Equal("123456789", token);
+ }
+
+ [Fact]
+ public void MakePossibleNames_Works()
+ {
+ Assert.Equal(["review-app-23765", "ra-23765", "ra23765"],
+ AzureCleaner.MakePossibleNames([23765]));
+ Assert.Equal(["review-app-23765", "ra-23765", "ra23765", "review-app-50", "ra-50", "ra50"],
+ AzureCleaner.MakePossibleNames([23765, 50]));
+ }
+
+ [Fact]
+ public void NameMatchesExpectedFormat_Works()
+ {
+ var possibleNames = AzureCleaner.MakePossibleNames([23765]);
+
+ // works for all in exact format
+ var modified = possibleNames;
+ Assert.All(modified, pn => AzureCleaner.NameMatchesExpectedFormat(possibleNames, pn));
+
+ // works when prefixed
+ modified = possibleNames.Select(pn => $"bla:{pn}").ToList();
+ Assert.All(modified, pn => AzureCleaner.NameMatchesExpectedFormat(possibleNames, pn));
+
+ // works when suffixed
+ modified = possibleNames.Select(pn => $"{pn}:bla").ToList();
+ Assert.All(modified, pn => AzureCleaner.NameMatchesExpectedFormat(possibleNames, pn));
+
+ // works for AppServicePlan
+ var resourceId = new ResourceIdentifier($"/subscriptions/{Guid.Empty}/resourceGroups/FABRIKAM/providers/Microsoft.Web/serverfarms/fabrikam-sites-ra23765");
+ Assert.True(AzureCleaner.NameMatchesExpectedFormat(possibleNames, resourceId));
+
+ // works for ManagedEnvironment
+ resourceId = new ResourceIdentifier($"/subscriptions/{Guid.Empty}/resourceGroups/FABRIKAM/providers/Microsoft.App/managedEnvironments/fabrikam-sites-ra-23765");
+ Assert.True(AzureCleaner.NameMatchesExpectedFormat(possibleNames, resourceId));
+
+ // works for Azure SQL Database
+ resourceId = new ResourceIdentifier($"/subscriptions/{Guid.Empty}/resourceGroups/FABRIKAM/providers/Microsoft.Sql/servers/fabrikam/databases/fabrikam-sites-ra-23765");
+ Assert.True(AzureCleaner.NameMatchesExpectedFormat(possibleNames, resourceId));
+
+ // skips Azure SQL Database master database
+ resourceId = new ResourceIdentifier($"/subscriptions/{Guid.Empty}/resourceGroups/FABRIKAM/providers/Microsoft.Sql/servers/fabrikam/databases/master");
+ Assert.False(AzureCleaner.NameMatchesExpectedFormat(possibleNames, resourceId));
+ }
+
+ [Fact]
+ public async Task HandleAsync_Works()
+ {
+ var cache = new MemoryCache(Options.Create(new MemoryCacheOptions()));
+ var optionsAccessor = Options.Create(new AzureCleanerOptions
+ {
+ AzdoProjects = ["https://dev.azure.com/fabrikam/DefaultCollection;123456789"],
+ });
+ var logger = new LoggerFactory().CreateLogger();
+ var cleaner = new ModifiedAzureCleaner(cache, optionsAccessor, logger);
+
+ await cleaner.HandleAsync(prId: 1,
+ remoteUrl: "https://dev.azure.com/fabrikam/DefaultCollection/_git/Fabrikam",
+ rawProjectUrl: "https://dev.azure.com/fabrikam/DefaultCollection/_apis/projects/6ce954b1-ce1f-45d1-b94d-e6bf2464ba2c");
+ var possibleNames1 = Assert.Single(cleaner.DeleteAzureResourcesAsyncCalls);
+ Assert.Equal(["review-app-1", "ra-1", "ra1"], possibleNames1);
+ var (url, token, possibleNames2) = Assert.Single(cleaner.DeleteReviewAppsEnvironmentsAsyncCalls);
+ Assert.Equal("https://dev.azure.com/fabrikam/DefaultCollection", url);
+ Assert.Equal("123456789", token);
+ Assert.Equal(["review-app-1", "ra-1", "ra1"], possibleNames2);
+ }
+
+ class ModifiedAzureCleaner(IMemoryCache cache, IOptions options, ILogger logger) : AzureCleaner(cache, options, logger)
+ {
+ public List> DeleteAzureResourcesAsyncCalls { get; } = [];
+ public List<(AzdoProjectUrl url, string? token, IReadOnlyCollection possibleNames)> DeleteReviewAppsEnvironmentsAsyncCalls { get; } = [];
+
+ protected override Task DeleteAzureResourcesAsync(IReadOnlyCollection possibleNames, CancellationToken cancellationToken = default)
+ {
+ DeleteAzureResourcesAsyncCalls.Add(possibleNames);
+ return Task.CompletedTask;
+ }
+
+ protected override Task DeleteReviewAppsEnvironmentsAsync(AzdoProjectUrl url, string token, IReadOnlyCollection possibleNames, CancellationToken cancellationToken)
+ {
+ DeleteReviewAppsEnvironmentsAsyncCalls.Add((url, token, possibleNames));
+ return Task.CompletedTask;
+ }
+ }
+}
diff --git a/Tingle.AzdoCleaner.Tests/EndpointTests.cs b/Tingle.AzureCleaner.Tests/EndpointTests.cs
similarity index 86%
rename from Tingle.AzdoCleaner.Tests/EndpointTests.cs
rename to Tingle.AzureCleaner.Tests/EndpointTests.cs
index 34b544f..04d4d16 100644
--- a/Tingle.AzdoCleaner.Tests/EndpointTests.cs
+++ b/Tingle.AzureCleaner.Tests/EndpointTests.cs
@@ -14,7 +14,7 @@
using Xunit;
using Xunit.Abstractions;
-namespace Tingle.AzdoCleaner.Tests;
+namespace Tingle.AzureCleaner.Tests;
public class EndpointTests(ITestOutputHelper outputHelper)
{
@@ -122,7 +122,7 @@ private async Task TestAsync(Func logic)
{
builder.AddInMemoryCollection(new Dictionary
{
- ["Handler:Projects:0"] = "https://dev.azure.com/fabrikam/DefaultCollection;123456789",
+ ["Cleaner:AzdoProjects:0"] = "https://dev.azure.com/fabrikam/DefaultCollection;123456789",
["Authentication:ServiceHooks:Credentials:vsts"] = "burp-bump",
});
})
@@ -130,12 +130,12 @@ private async Task TestAsync(Func logic)
{
var configuration = context.Configuration;
services.AddRouting();
- services.AddNotificationsHandler(configuration.GetSection("Handler"));
- services.AddSingleton();
+ services.AddCleaner(configuration.GetSection("Cleaner"));
+ services.AddSingleton();
services.AddEventBus(builder => builder.AddInMemoryTransport().AddInMemoryTestHarness());
services.AddAuthentication()
- .AddBasic(options => options.Realm = "AzdoCleaner");
+ .AddBasic(options => options.Realm = "AzureCleaner");
services.AddAuthorization(options => options.FallbackPolicy = options.DefaultPolicy);
})
@@ -173,13 +173,20 @@ private async Task TestAsync(Func logic)
}
}
- class ModifiedAzdoEventHandler(IMemoryCache cache, IOptions options, ILogger logger) : AzdoEventHandler(cache, options, logger)
+ class ModifiedAzureCleaner(IMemoryCache cache, IOptions options, ILogger logger) : AzureCleaner(cache, options, logger)
{
- public List<(AzdoProjectUrl url, string? token, IEnumerable prIds)> Calls { get; } = [];
+ public List> DeleteAzureResourcesAsyncCalls { get; } = [];
+ public List<(AzdoProjectUrl url, string? token, IReadOnlyCollection possibleNames)> DeleteReviewAppsEnvironmentsAsyncCalls { get; } = [];
- protected override Task DeleteReviewAppResourcesAsync(AzdoProjectUrl url, string? token, IEnumerable prIds, CancellationToken cancellationToken = default)
+ protected override Task DeleteAzureResourcesAsync(IReadOnlyCollection possibleNames, CancellationToken cancellationToken = default)
{
- Calls.Add((url, token, prIds));
+ DeleteAzureResourcesAsyncCalls.Add(possibleNames);
+ return Task.CompletedTask;
+ }
+
+ protected override Task DeleteReviewAppsEnvironmentsAsync(AzdoProjectUrl url, string token, IReadOnlyCollection possibleNames, CancellationToken cancellationToken)
+ {
+ DeleteReviewAppsEnvironmentsAsyncCalls.Add((url, token, possibleNames));
return Task.CompletedTask;
}
}
diff --git a/Tingle.AzdoCleaner.Tests/Samples/git.pullrequest.updated.json b/Tingle.AzureCleaner.Tests/Samples/git.pullrequest.updated.json
similarity index 100%
rename from Tingle.AzdoCleaner.Tests/Samples/git.pullrequest.updated.json
rename to Tingle.AzureCleaner.Tests/Samples/git.pullrequest.updated.json
diff --git a/Tingle.AzdoCleaner.Tests/TestSamples.cs b/Tingle.AzureCleaner.Tests/TestSamples.cs
similarity index 90%
rename from Tingle.AzdoCleaner.Tests/TestSamples.cs
rename to Tingle.AzureCleaner.Tests/TestSamples.cs
index fc8c6c5..616ccd7 100644
--- a/Tingle.AzdoCleaner.Tests/TestSamples.cs
+++ b/Tingle.AzureCleaner.Tests/TestSamples.cs
@@ -1,4 +1,4 @@
-namespace Tingle.AzdoCleaner.Tests;
+namespace Tingle.AzureCleaner.Tests;
internal class TestSamples
{
diff --git a/Tingle.AzdoCleaner.Tests/Tingle.AzdoCleaner.Tests.csproj b/Tingle.AzureCleaner.Tests/Tingle.AzureCleaner.Tests.csproj
similarity index 90%
rename from Tingle.AzdoCleaner.Tests/Tingle.AzdoCleaner.Tests.csproj
rename to Tingle.AzureCleaner.Tests/Tingle.AzureCleaner.Tests.csproj
index 8a51b78..6d28e1b 100644
--- a/Tingle.AzdoCleaner.Tests/Tingle.AzdoCleaner.Tests.csproj
+++ b/Tingle.AzureCleaner.Tests/Tingle.AzureCleaner.Tests.csproj
@@ -19,7 +19,7 @@
-
+
diff --git a/Tingle.AzdoCleaner/AzdoEvent.cs b/Tingle.AzureCleaner/AzdoEvent.cs
similarity index 98%
rename from Tingle.AzdoCleaner/AzdoEvent.cs
rename to Tingle.AzureCleaner/AzdoEvent.cs
index 4cdbefc..f6b73b2 100644
--- a/Tingle.AzdoCleaner/AzdoEvent.cs
+++ b/Tingle.AzureCleaner/AzdoEvent.cs
@@ -3,7 +3,7 @@
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
-namespace Tingle.AzdoCleaner;
+namespace Tingle.AzureCleaner;
public class AzdoEvent
{
diff --git a/Tingle.AzdoCleaner/AzdoProjectUrl.cs b/Tingle.AzureCleaner/AzdoProjectUrl.cs
similarity index 98%
rename from Tingle.AzdoCleaner/AzdoProjectUrl.cs
rename to Tingle.AzureCleaner/AzdoProjectUrl.cs
index 2835155..a8bf54a 100644
--- a/Tingle.AzdoCleaner/AzdoProjectUrl.cs
+++ b/Tingle.AzureCleaner/AzdoProjectUrl.cs
@@ -1,6 +1,6 @@
using System.Diagnostics.CodeAnalysis;
-namespace Tingle.AzdoCleaner;
+namespace Tingle.AzureCleaner;
public readonly struct AzdoProjectUrl : IEquatable
{
diff --git a/Tingle.AzdoCleaner/AzdoEventHandler.cs b/Tingle.AzureCleaner/AzureCleaner.cs
similarity index 96%
rename from Tingle.AzdoCleaner/AzdoEventHandler.cs
rename to Tingle.AzureCleaner/AzureCleaner.cs
index 79cb3e4..efbef61 100644
--- a/Tingle.AzdoCleaner/AzdoEventHandler.cs
+++ b/Tingle.AzureCleaner/AzureCleaner.cs
@@ -24,56 +24,60 @@
using System.Security.Cryptography;
using System.Text;
-namespace Tingle.AzdoCleaner;
+namespace Tingle.AzureCleaner;
-internal class AzdoEventHandler
+internal class AzureCleaner
{
private readonly IMemoryCache cache;
- private readonly AzureDevOpsEventHandlerOptions options;
+ private readonly AzureCleanerOptions options;
private readonly ILogger logger;
- private readonly Dictionary projects;
+ private readonly Dictionary azdoProjects;
- public AzdoEventHandler(IMemoryCache cache, IOptions options, ILogger logger)
+ public AzureCleaner(IMemoryCache cache, IOptions options, ILogger logger)
{
this.cache = cache ?? throw new ArgumentNullException(nameof(cache));
this.options = options?.Value ?? throw new ArgumentNullException(nameof(options));
this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
- projects = this.options.Projects.Select(e => e.Split(";")).ToDictionary(s => s[0], s => s[1]);
+ azdoProjects = this.options.AzdoProjects.Select(e => e.Split(";")).ToDictionary(s => s[0], s => s[1]);
}
- public virtual async Task HandleAsync(int prId, string remoteUrl, string rawProjectUrl, CancellationToken cancellationToken = default)
+ public virtual async Task HandleAsync(int prId, string? remoteUrl = null, string? rawProjectUrl = null, CancellationToken cancellationToken = default)
{
- ArgumentNullException.ThrowIfNull(remoteUrl);
- ArgumentNullException.ThrowIfNull(rawProjectUrl);
+ var possibleNames = MakePossibleNames([prId]);
- if (!TryFindProject(rawProjectUrl, out var url, out var token)
- && !TryFindProject(remoteUrl, out url, out token))
+ if (remoteUrl is not null || rawProjectUrl is not null)
{
- logger.LogWarning("Project for '{ProjectUrl}' or '{RemoteUrl}' does not have a token configured.", rawProjectUrl, remoteUrl);
+ if (TryFindAzdoProject(rawProjectUrl, out var url, out var token)
+ || TryFindAzdoProject(remoteUrl, out url, out token))
+ {
+ await DeleteReviewAppsEnvironmentsAsync(url, token, possibleNames, cancellationToken);
+ }
+ else
+ {
+ logger.LogWarning("Project for '{ProjectUrl}' or '{RemoteUrl}' does not have a token configured.", rawProjectUrl, remoteUrl);
+ }
}
- await DeleteReviewAppResourcesAsync(url, token, [prId], cancellationToken);
+ await DeleteAzureResourcesAsync(possibleNames, cancellationToken);
}
- internal virtual bool TryFindProject(string rawUrl, out AzdoProjectUrl url, [NotNullWhen(true)] out string? token)
+ internal virtual bool TryFindAzdoProject(string? rawUrl, out AzdoProjectUrl url, [NotNullWhen(true)] out string? token)
{
+ url = default;
+ token = default;
+ if (string.IsNullOrWhiteSpace(rawUrl)) return false;
+
url = (AzdoProjectUrl)rawUrl;
- return projects.TryGetValue(url, out token);
+ return azdoProjects.TryGetValue(url, out token);
}
- protected virtual async Task DeleteReviewAppResourcesAsync(AzdoProjectUrl url, string? token, IEnumerable prIds, CancellationToken cancellationToken = default)
+ protected virtual async Task DeleteAzureResourcesAsync(IReadOnlyCollection possibleNames, CancellationToken cancellationToken = default)
{
var credential = new DefaultAzureCredential();
var client = new ArmClient(credential);
- var possibleNames = MakePossibleNames(prIds);
- if (token is not null)
- {
- await DeleteReviewAppsEnvironmentsAsync(url, token, possibleNames, cancellationToken);
- }
-
logger.LogDebug("Finding azure subscriptions ...");
var subscriptions = client.GetSubscriptions().GetAllAsync(cancellationToken);
await foreach (var sub in subscriptions)
@@ -141,6 +145,7 @@ protected virtual async Task DeleteReviewAppResourcesAsync(AzdoProjectUrl url, s
}
}
}
+
protected virtual async Task DeleteAzureResourceGroupsAsync(SubscriptionResource sub, IReadOnlyCollection possibleNames, CancellationToken cancellationToken)
{
var groups = sub.GetResourceGroups();
@@ -858,9 +863,9 @@ static string hash(string v)
}
}
-public class AzureDevOpsEventHandlerOptions
+public class AzureCleanerOptions
{
- public List Projects { get; set; } = [];
+ public List AzdoProjects { get; set; } = [];
public bool AzureResourceGroups { get; set; } = true;
public bool AzureKubernetes { get; set; } = true;
diff --git a/Tingle.AzdoCleaner/BasicUserValidationService.cs b/Tingle.AzureCleaner/BasicUserValidationService.cs
similarity index 93%
rename from Tingle.AzdoCleaner/BasicUserValidationService.cs
rename to Tingle.AzureCleaner/BasicUserValidationService.cs
index 807699a..ff1e17c 100644
--- a/Tingle.AzdoCleaner/BasicUserValidationService.cs
+++ b/Tingle.AzureCleaner/BasicUserValidationService.cs
@@ -1,6 +1,6 @@
using AspNetCore.Authentication.Basic;
-namespace Tingle.AzdoCleaner;
+namespace Tingle.AzureCleaner;
internal class BasicUserValidationService(IConfiguration configuration) : IBasicUserValidationService
{
diff --git a/Tingle.AzdoCleaner/Dockerfile b/Tingle.AzureCleaner/Dockerfile
similarity index 60%
rename from Tingle.AzdoCleaner/Dockerfile
rename to Tingle.AzureCleaner/Dockerfile
index c2a6b41..6adbf62 100644
--- a/Tingle.AzdoCleaner/Dockerfile
+++ b/Tingle.AzureCleaner/Dockerfile
@@ -7,23 +7,23 @@ EXPOSE 8080
EXPOSE 8081
LABEL org.opencontainers.image.description="Cleanup tool for Azure resources based on Azure DevOps PRs" \
- org.opencontainers.image.source="https://github.com/tinglesoftware/azure-devops-cleaner"
+ org.opencontainers.image.source="https://github.com/tinglesoftware/azure-resources-cleaner"
FROM mcr.microsoft.com/dotnet/sdk:8.0-bookworm-slim AS build
ARG BUILD_CONFIGURATION=Release
WORKDIR /src
COPY ["Directory.Build.props", "."]
-COPY ["Tingle.AzdoCleaner/Tingle.AzdoCleaner.csproj", "Tingle.AzdoCleaner/"]
-RUN dotnet restore "./Tingle.AzdoCleaner/./Tingle.AzdoCleaner.csproj"
+COPY ["Tingle.AzureCleaner/Tingle.AzureCleaner.csproj", "Tingle.AzureCleaner/"]
+RUN dotnet restore "./Tingle.AzureCleaner/./Tingle.AzureCleaner.csproj"
COPY . .
-WORKDIR "/src/Tingle.AzdoCleaner"
-RUN dotnet build "./Tingle.AzdoCleaner.csproj" -c $BUILD_CONFIGURATION -o /app/build
+WORKDIR "/src/Tingle.AzureCleaner"
+RUN dotnet build "./Tingle.AzureCleaner.csproj" -c $BUILD_CONFIGURATION -o /app/build
FROM build AS publish
ARG BUILD_CONFIGURATION=Release
-RUN dotnet publish "./Tingle.AzdoCleaner.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
+RUN dotnet publish "./Tingle.AzureCleaner.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false
FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
-ENTRYPOINT ["dotnet", "Tingle.AzdoCleaner.dll"]
\ No newline at end of file
+ENTRYPOINT ["dotnet", "Tingle.AzureCleaner.dll"]
diff --git a/Tingle.AzdoCleaner/Program.cs b/Tingle.AzureCleaner/Program.cs
similarity index 89%
rename from Tingle.AzdoCleaner/Program.cs
rename to Tingle.AzureCleaner/Program.cs
index c9bfcf1..70d8bfa 100644
--- a/Tingle.AzdoCleaner/Program.cs
+++ b/Tingle.AzureCleaner/Program.cs
@@ -3,7 +3,7 @@
using Microsoft.AspNetCore.Mvc;
using MiniValidation;
using System.Text.Json;
-using Tingle.AzdoCleaner;
+using Tingle.AzureCleaner;
using Tingle.EventBus;
var builder = WebApplication.CreateBuilder(args);
@@ -23,7 +23,7 @@
builder.Services.AddApplicationInsightsTelemetry();
builder.Services.AddAuthentication()
- .AddBasic(options => options.Realm = "AzdoCleaner");
+ .AddBasic(options => options.Realm = "AzureCleaner");
builder.Services.AddAuthorization(options =>
{
@@ -31,7 +31,7 @@
options.FallbackPolicy = options.DefaultPolicy;
});
-builder.Services.AddNotificationsHandler(builder.Configuration.GetSection("Handler"));
+builder.Services.AddCleaner(builder.Configuration.GetSection("Cleaner"));
// Add event bus
var selectedTransport = builder.Configuration.GetValue("EventBus:SelectedTransport");
@@ -79,11 +79,11 @@ internal enum EventBusTransportKind { InMemory, ServiceBus, QueueStorage, }
internal static class IServiceCollectionExtensions
{
- public static IServiceCollection AddNotificationsHandler(this IServiceCollection services, IConfiguration configuration)
+ public static IServiceCollection AddCleaner(this IServiceCollection services, IConfiguration configuration)
{
services.AddMemoryCache();
- services.Configure(configuration);
- services.AddSingleton();
+ services.Configure(configuration);
+ services.AddSingleton();
return services;
}
@@ -95,7 +95,7 @@ public static IEndpointConventionBuilder MapWebhooksAzure(this IEndpointRouteBui
{
return builder.MapPost("/webhooks/azure", async (ILoggerFactory loggerFactory, IEventPublisher publisher, [FromBody] AzdoEvent model) =>
{
- var logger = loggerFactory.CreateLogger("Tingle.AzdoCleaner.Webhooks");
+ var logger = loggerFactory.CreateLogger("Tingle.AzureCleaner.Webhooks");
if (!MiniValidator.TryValidate(model, out var errors)) return Results.ValidationProblem(errors);
var type = model.EventType;
@@ -139,7 +139,7 @@ public static IEndpointConventionBuilder MapWebhooksAzure(this IEndpointRouteBui
else
{
logger.LogWarning("Events of type {EventType} are not supported." +
- " If you wish to support them you can clone the repository or contribute a PR at https://github.com/tinglesoftware/azure-devops-cleaner",
+ " If you wish to support them you can clone the repository or contribute a PR at https://github.com/tinglesoftware/azure-resources-cleaner",
type);
}
@@ -155,12 +155,12 @@ internal class AzdoCleanupEvent
public required string RawProjectUrl { get; init; }
}
-internal class ProcessAzdoCleanupEventConsumer(AzdoEventHandler handler) : IEventConsumer
-{
+internal class ProcessAzdoCleanupEventConsumer(AzureCleaner cleaner) : IEventConsumer
+{
public async Task ConsumeAsync(EventContext context, CancellationToken cancellationToken)
{
var evt = context.Event;
- await handler.HandleAsync(prId: evt.PullRequestId,
+ await cleaner.HandleAsync(prId: evt.PullRequestId,
remoteUrl: evt.RemoteUrl,
rawProjectUrl: evt.RawProjectUrl,
cancellationToken: cancellationToken);
diff --git a/Tingle.AzdoCleaner/Properties/launchSettings.json b/Tingle.AzureCleaner/Properties/launchSettings.json
similarity index 95%
rename from Tingle.AzdoCleaner/Properties/launchSettings.json
rename to Tingle.AzureCleaner/Properties/launchSettings.json
index 8995fe7..7b40e20 100644
--- a/Tingle.AzdoCleaner/Properties/launchSettings.json
+++ b/Tingle.AzureCleaner/Properties/launchSettings.json
@@ -1,7 +1,7 @@
{
"$schema": "http://json.schemastore.org/launchsettings.json",
"profiles": {
- "Tingle.AzdoCleaner": {
+ "Tingle.AzureCleaner": {
"commandName": "Project",
"launchBrowser": true,
"launchUrl": "health",
diff --git a/Tingle.AzdoCleaner/Tingle.AzdoCleaner.csproj b/Tingle.AzureCleaner/Tingle.AzureCleaner.csproj
similarity index 97%
rename from Tingle.AzdoCleaner/Tingle.AzdoCleaner.csproj
rename to Tingle.AzureCleaner/Tingle.AzureCleaner.csproj
index baf0051..c83d47f 100644
--- a/Tingle.AzdoCleaner/Tingle.AzdoCleaner.csproj
+++ b/Tingle.AzureCleaner/Tingle.AzureCleaner.csproj
@@ -7,7 +7,7 @@
-
+
diff --git a/Tingle.AzdoCleaner/appsettings.Development.json b/Tingle.AzureCleaner/appsettings.Development.json
similarity index 100%
rename from Tingle.AzdoCleaner/appsettings.Development.json
rename to Tingle.AzureCleaner/appsettings.Development.json
diff --git a/Tingle.AzdoCleaner/appsettings.json b/Tingle.AzureCleaner/appsettings.json
similarity index 97%
rename from Tingle.AzdoCleaner/appsettings.json
rename to Tingle.AzureCleaner/appsettings.json
index b9e8072..1e7be48 100644
--- a/Tingle.AzdoCleaner/appsettings.json
+++ b/Tingle.AzureCleaner/appsettings.json
@@ -42,8 +42,8 @@
}
},
- "Handler": {
- "Projects": [
+ "Cleaner": {
+ "AzdoProjects": [
"https://dev.azure.com/fabrikam/DefaultCollection;"
],
diff --git a/main.bicep b/main.bicep
index bb7755b..a24e693 100644
--- a/main.bicep
+++ b/main.bicep
@@ -2,7 +2,7 @@
param location string = resourceGroup().location
@description('Name of all resources.')
-param name string = 'azdo-cleaner'
+param name string = 'azure-cleaner'
@description('Tag of the docker image.')
param dockerImageTag string = '#{DOCKER_IMAGE_TAG}#'
@@ -176,8 +176,8 @@ resource app 'Microsoft.App/containerApps@2023-05-01' = {
template: {
containers: [
{
- image: 'ghcr.io/tinglesoftware/azure-devops-cleaner:${dockerImageTag}'
- name: 'azdo-cleaner'
+ image: 'ghcr.io/tinglesoftware/azure-resources-cleaner:${dockerImageTag}'
+ name: 'azure-cleaner'
env: [
{ name: 'AZURE_CLIENT_ID', value: managedIdentity.properties.clientId } // Specifies the User-Assigned Managed Identity to use. Without this, the app attempt to use the system assigned one.
{ name: 'ASPNETCORE_FORWARDEDHEADERS_ENABLED', value: 'true' }
@@ -185,7 +185,7 @@ resource app 'Microsoft.App/containerApps@2023-05-01' = {
{ name: 'ApplicationInsights__ConnectionString', secretRef: 'connection-strings-application-insights' }
{ name: 'Authentication__ServiceHooks__Credentials__vsts', secretRef: 'notifications-password' }
- { name: 'Handler__Projects__0', secretRef: 'project-and-token-0' }
+ { name: 'Cleaner__AzdoProjects__0', secretRef: 'project-and-token-0' }
{ name: 'EventBus__SelectedTransport', value: eventBusTransport }
{
diff --git a/main.json b/main.json
index 48271e7..8e82b08 100644
--- a/main.json
+++ b/main.json
@@ -11,7 +11,7 @@
},
"name": {
"type": "string",
- "defaultValue": "azdo-cleaner",
+ "defaultValue": "azure-cleaner",
"metadata": {
"description": "Name of all resources."
}
@@ -200,8 +200,8 @@
"template": {
"containers": [
{
- "image": "[format('ghcr.io/tinglesoftware/azure-devops-cleaner:{0}', parameters('dockerImageTag'))]",
- "name": "azdo-cleaner",
+ "image": "[format('ghcr.io/tinglesoftware/azure-resources-cleaner:{0}', parameters('dockerImageTag'))]",
+ "name": "azure-cleaner",
"env": [
{
"name": "AZURE_CLIENT_ID",
@@ -220,7 +220,7 @@
"secretRef": "notifications-password"
},
{
- "name": "Handler__Projects__0",
+ "name": "Cleaner__AzdoProjects__0",
"secretRef": "project-and-token-0"
},
{