Skip to content

Commit d389d79

Browse files
Register MAR as default trusted repository (#1955)
1 parent ff881f4 commit d389d79

15 files changed

Lines changed: 366 additions & 74 deletions

src/code/RegisterPSResourceRepository.cs

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,19 @@ class RegisterPSResourceRepository : PSCmdlet, IDynamicParameters
2929
#region Members
3030

3131
private readonly string PSGalleryRepoName = "PSGallery";
32+
private readonly string MicrosoftArtifactRegistryRepoName = "MicrosoftArtifactRegistry";
33+
private readonly string MicrosoftArtifactRegistryRepoUri = "https://mcr.microsoft.com";
3234
private readonly string PSGalleryRepoUri = "https://www.powershellgallery.com/api/v2";
3335
private const int DefaultPriority = 50;
3436
private const bool DefaultTrusted = false;
37+
private const bool MARDefaultTrusted = true;
38+
private const int MARDefaultPriority = 40;
3539
private const string NameParameterSet = "NameParameterSet";
3640
private const string PSGalleryParameterSet = "PSGalleryParameterSet";
3741
private const string RepositoriesParameterSet = "RepositoriesParameterSet";
3842
private Uri _uri;
3943
private CredentialProviderDynamicParameters _credentialProvider;
44+
private const string MARParameterSet = "MARParameterSet";
4045

4146
#endregion
4247

@@ -62,6 +67,13 @@ class RegisterPSResourceRepository : PSCmdlet, IDynamicParameters
6267
[Parameter(Mandatory = true, ParameterSetName = PSGalleryParameterSet, HelpMessage = "PSGallery switch used to indicate registering PSGallery repository.")]
6368
public SwitchParameter PSGallery { get; set; }
6469

70+
/// <summary>
71+
/// When specified, registers Microsoft Artifact Registry repository.
72+
/// </summary>
73+
[Parameter(Mandatory = true, ParameterSetName = MARParameterSet, HelpMessage = "Switch used to indicate registering Microsoft Artifact Registry repository.")]
74+
[Alias("MAR")]
75+
public SwitchParameter MicrosoftArtifactRegistry { get; set; }
76+
6577
/// <summary>
6678
/// Specifies a hashtable of repositories and is used to register multiple repositories at once.
6779
/// </summary>
@@ -74,6 +86,7 @@ class RegisterPSResourceRepository : PSCmdlet, IDynamicParameters
7486
/// </summary>
7587
[Parameter(ParameterSetName = NameParameterSet)]
7688
[Parameter(ParameterSetName = PSGalleryParameterSet)]
89+
[Parameter(ParameterSetName = MARParameterSet)]
7790
public SwitchParameter Trusted { get; set; }
7891

7992
/// <summary>
@@ -84,8 +97,9 @@ class RegisterPSResourceRepository : PSCmdlet, IDynamicParameters
8497
/// </summary>
8598
[Parameter(ParameterSetName = NameParameterSet)]
8699
[Parameter(ParameterSetName = PSGalleryParameterSet)]
100+
[Parameter(ParameterSetName = MARParameterSet)]
87101
[ValidateRange(0, 100)]
88-
public int Priority { get; set; } = DefaultPriority;
102+
public int Priority { get; set; }
89103

90104
/// <summary>
91105
/// Specifies the Api version of the repository to be set.
@@ -122,6 +136,7 @@ public object GetDynamicParameters()
122136
// It should also not appear when using the 'Repositories' parameter set.
123137
if (ParameterSetName.Equals(PSGalleryParameterSet) ||
124138
ParameterSetName.Equals(RepositoriesParameterSet) ||
139+
ParameterSetName.Equals(MARParameterSet) ||
125140
PSRepositoryInfo.IsValidContainerRegistryURL(Uri))
126141
{
127142
return null;
@@ -149,6 +164,18 @@ protected override void ProcessRecord()
149164
repoApiVersion = ApiVersion;
150165
}
151166

167+
if (!MyInvocation.BoundParameters.ContainsKey(nameof(Priority)))
168+
{
169+
if (ParameterSetName.Equals(MARParameterSet, StringComparison.OrdinalIgnoreCase))
170+
{
171+
Priority = MARDefaultPriority;
172+
}
173+
else
174+
{
175+
Priority = DefaultPriority;
176+
}
177+
}
178+
152179
PSRepositoryInfo.CredentialProviderType? credentialProvider = _credentialProvider?.CredentialProvider;
153180

154181
switch (ParameterSetName)
@@ -218,6 +245,25 @@ protected override void ProcessRecord()
218245
}
219246
break;
220247

248+
case MARParameterSet:
249+
if (MicrosoftArtifactRegistry)
250+
{
251+
try
252+
{
253+
bool trustedValue = Trusted.IsPresent ? Trusted : MARDefaultTrusted;
254+
items.Add(MicrosoftArtifactRegistryParameterSetHelper(Priority, trustedValue));
255+
}
256+
catch (Exception e)
257+
{
258+
ThrowTerminatingError(new ErrorRecord(
259+
new PSInvalidOperationException(e.Message),
260+
"ErrorInMARParameterSet",
261+
ErrorCategory.InvalidArgument,
262+
this));
263+
}
264+
}
265+
break;
266+
221267
default:
222268
Dbg.Assert(false, "Invalid parameter set");
223269
break;
@@ -262,6 +308,34 @@ private PSRepositoryInfo PSGalleryParameterSetHelper(int repoPriority, bool repo
262308
return addedRepo;
263309
}
264310

311+
private PSRepositoryInfo MicrosoftArtifactRegistryParameterSetHelper(int repoPriority, bool repoTrusted)
312+
{
313+
WriteDebug("In RegisterPSResourceRepository::MicrosoftArtifactRegistryParameterSetHelper()");
314+
Uri marUri = new Uri(MicrosoftArtifactRegistryRepoUri);
315+
WriteDebug("Internal name and uri values for Microsoft Artifact Registry are hardcoded and validated. Priority and trusted values, if passed in, also validated");
316+
var addedRepo = RepositorySettings.AddToRepositoryStore(MicrosoftArtifactRegistryRepoName,
317+
marUri,
318+
repoPriority,
319+
repoTrusted,
320+
apiVersion: null,
321+
repoCredentialInfo: null,
322+
credentialProvider: null,
323+
Force,
324+
this,
325+
out string errorMsg);
326+
327+
if (!string.IsNullOrEmpty(errorMsg))
328+
{
329+
ThrowTerminatingError(new ErrorRecord(
330+
new PSInvalidOperationException(errorMsg),
331+
"RepositoryCredentialSecretManagementUnavailableModule",
332+
ErrorCategory.ResourceUnavailable,
333+
this));
334+
}
335+
336+
return addedRepo;
337+
}
338+
265339
private List<PSRepositoryInfo> RepositoriesParameterSetHelper()
266340
{
267341
WriteDebug("In RegisterPSResourceRepository::RepositoriesParameterSetHelper()");
@@ -323,7 +397,7 @@ private PSRepositoryInfo RepoValidationHelper(Hashtable repo)
323397
return null;
324398
}
325399

326-
if (repo["Name"].ToString().Equals("PSGallery"))
400+
if (repo["Name"].ToString().Equals("PSGallery", StringComparison.OrdinalIgnoreCase))
327401
{
328402
WriteError(new ErrorRecord(
329403
new PSInvalidOperationException("Cannot register PSGallery with -Name parameter. Try: Register-PSResourceRepository -PSGallery"),
@@ -334,6 +408,17 @@ private PSRepositoryInfo RepoValidationHelper(Hashtable repo)
334408
return null;
335409
}
336410

411+
if (repo["Name"].ToString().Equals("MAR", StringComparison.OrdinalIgnoreCase))
412+
{
413+
WriteError(new ErrorRecord(
414+
new PSInvalidOperationException("Cannot register MAR with -Name parameter. The MAR repository is automatically registered. Try: Reset-PSResourceRepository to restore default repositories."),
415+
"MARProvidedAsNameRepoPSet",
416+
ErrorCategory.InvalidArgument,
417+
this));
418+
419+
return null;
420+
}
421+
337422
if (!repo.ContainsKey("Uri") || repo["Uri"] == null || String.IsNullOrEmpty(repo["Uri"].ToString()))
338423
{
339424
WriteError(new ErrorRecord(

src/code/RepositorySettings.cs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,12 @@ internal static class RepositorySettings
2626
// The repository store file's location is currently only at '%LOCALAPPDATA%\PSResourceGet' for the user account.
2727
private const string PSGalleryRepoName = "PSGallery";
2828
private const string PSGalleryRepoUri = "https://www.powershellgallery.com/api/v2";
29+
private const string MARRepoName = "MicrosoftArtifactRegistry";
30+
private const string MARRepoUri = "https://mcr.microsoft.com";
2931
private const int DefaultPriority = 50;
32+
private const int MARDefaultPriority = 40;
3033
private const bool DefaultTrusted = false;
34+
private const bool MARDefaultTrusted = true;
3135
private const string RepositoryFileName = "PSResourceRepository.xml";
3236
private static readonly string RepositoryPath = Path.Combine(Environment.GetFolderPath(
3337
Environment.SpecialFolder.LocalApplicationData), "PSResourceGet");
@@ -63,6 +67,10 @@ public static void CheckRepositoryStore()
6367
// Add PSGallery to the newly created store
6468
Uri psGalleryUri = new Uri(PSGalleryRepoUri);
6569
Add(PSGalleryRepoName, psGalleryUri, DefaultPriority, DefaultTrusted, repoCredentialInfo: null, repoCredentialProvider: CredentialProviderType.None, APIVersion.V2, force: false);
70+
71+
// Add MAR to the newly created store
72+
Uri marUri = new Uri(MARRepoUri);
73+
Add(MARRepoName, marUri, MARDefaultPriority, MARDefaultTrusted, repoCredentialInfo: null, repoCredentialProvider: CredentialProviderType.None, APIVersion.ContainerRegistry, force: false);
6674
}
6775

6876
// Open file (which should exist now), if cannot/is corrupted then throw error
@@ -85,6 +93,12 @@ public static PSRepositoryInfo AddRepository(string repoName, Uri repoUri, int r
8593
return null;
8694
}
8795

96+
if (repoName.Equals("MicrosoftArtifactRegistry", StringComparison.OrdinalIgnoreCase))
97+
{
98+
errorMsg = "Cannot register MAR with -Name parameter. Try: Register-PSResourceRepository -MicrosoftArtifactRegistry.";
99+
return null;
100+
}
101+
88102
return AddToRepositoryStore(repoName, repoUri, repoPriority, repoTrusted, apiVersion, repoCredentialInfo, repoCredentialProvider, force, cmdletPassedIn, out errorMsg);
89103
}
90104

@@ -187,6 +201,20 @@ public static PSRepositoryInfo UpdateRepositoryStore(string repoName, Uri repoUr
187201
return null;
188202
}
189203

204+
// check MAR Uri is not trying to be set
205+
if (repoName.Equals("MicrosoftArtifactRegistry", StringComparison.OrdinalIgnoreCase) && repoUri != null)
206+
{
207+
errorMsg = "The MAR repository has a predefined Uri. Setting the -Uri parameter for this repository is not allowed. Please run 'Reset-PSResourceRepository' to restore default repositories.";
208+
return null;
209+
}
210+
211+
// check MAR CredentialInfo is not trying to be set
212+
if (repoName.Equals("MicrosoftArtifactRegistry", StringComparison.OrdinalIgnoreCase) && repoCredentialInfo != null)
213+
{
214+
errorMsg = "Setting the -CredentialInfo parameter for MAR is not allowed. Run 'Reset-PSResourceRepository' to restore default repositories.";
215+
return null;
216+
}
217+
190218
// determine trusted value to pass in (true/false if set, null otherwise, hence the nullable bool variable)
191219
bool? _trustedNullable = isSet ? new bool?(repoTrusted) : new bool?();
192220

@@ -908,6 +936,10 @@ public static PSRepositoryInfo Reset(out string errorMsg)
908936
Uri psGalleryUri = new Uri(PSGalleryRepoUri);
909937
PSRepositoryInfo psGalleryRepo = Add(PSGalleryRepoName, psGalleryUri, DefaultPriority, DefaultTrusted, repoCredentialInfo: null, repoCredentialProvider: CredentialProviderType.None, APIVersion.V2, force: false);
910938

939+
// Add MAR to the newly created store
940+
Uri marUri = new Uri(MARRepoUri);
941+
Add(MARRepoName, marUri, MARDefaultPriority, MARDefaultTrusted, repoCredentialInfo: null, repoCredentialProvider: CredentialProviderType.None, APIVersion.ContainerRegistry, force: false);
942+
911943
// Clean up backup file on success
912944
if (!string.IsNullOrEmpty(backupFilePath) && File.Exists(backupFilePath))
913945
{

src/code/ResetPSResourceRepository.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace Microsoft.PowerShell.PSResourceGet.Cmdlets
1010
/// <summary>
1111
/// The Reset-PSResourceRepository cmdlet resets the repository store by creating a new PSRepositories.xml file.
1212
/// This is useful when the repository store becomes corrupted.
13-
/// It will create a new repository store with only the PSGallery repository registered.
13+
/// It will create a new repository store with PSGallery and MAR repositories registered.
1414
/// </summary>
1515
[Cmdlet(VerbsCommon.Reset,
1616
"PSResourceRepository",
@@ -39,8 +39,8 @@ protected override void ProcessRecord()
3939
"PSResourceRepository.xml");
4040

4141
WriteVerbose($"Resetting repository store at: {repositoryStorePath}");
42-
43-
if (!ShouldProcess(repositoryStorePath, "Reset repository store and create new PSRepositories.xml file with PSGallery registered"))
42+
43+
if (!ShouldProcess(repositoryStorePath, "Reset repository store and create new PSRepositories.xml file with PSGallery and MAR registered"))
4444
{
4545
return;
4646
}
@@ -57,7 +57,7 @@ protected override void ProcessRecord()
5757
return;
5858
}
5959

60-
WriteVerbose("Repository store reset successfully. PSGallery has been registered.");
60+
WriteVerbose("Repository store reset successfully. PSGallery and MAR have been registered.");
6161

6262
if (PassThru)
6363
{

src/code/SetPSResourceRepository.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,11 @@ public SwitchParameter Trusted
110110
public object GetDynamicParameters()
111111
{
112112
PSRepositoryInfo repository = RepositorySettings.Read(new[] { Name }, out string[] _).FirstOrDefault();
113-
// Dynamic parameter '-CredentialProvider' should not appear for PSGallery, or any container registry repository.
113+
// Dynamic parameter '-CredentialProvider' should not appear for PSGallery, MAR, or any container registry repository.
114114
// It should also not appear when using the 'Repositories' parameter set.
115115
if (repository is not null &&
116116
(repository.Name.Equals("PSGallery", StringComparison.OrdinalIgnoreCase) ||
117+
repository.Name.Equals("MicrosoftArtifactRegistry", StringComparison.OrdinalIgnoreCase) ||
117118
ParameterSetName.Equals(RepositoriesParameterSet) ||
118119
repository.IsContainerRegistry()))
119120
{

test/FindPSResourceTests/FindPSResourceContainerRegistryServer.Tests.ps1

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Import-Module $modPath -Force -Verbose
77
Describe 'Test HTTP Find-PSResource for ACR Server Protocol' -tags 'CI' {
88

99
BeforeAll {
10+
$MARName = Get-MarName
1011
$testModuleName = "test-module"
1112
$testModuleWith2DigitVersion = "test-2DigitPkg"
1213
$testModuleParentName = "test_parent_mod"
@@ -273,50 +274,51 @@ Describe 'Test HTTP Find-PSResource for ACR Server Protocol' -tags 'CI' {
273274
}
274275

275276
Describe 'Test Find-PSResource for MAR Repository' -tags 'CI' {
277+
276278
BeforeAll {
277-
Register-PSResourceRepository -Name "MAR" -Uri "https://mcr.microsoft.com" -ApiVersion "ContainerRegistry"
279+
Get-NewPSResourceRepositoryFile
278280
}
279281

280282
AfterAll {
281-
Unregister-PSResourceRepository -Name "MAR"
283+
Get-RevertPSResourceRepositoryFile
282284
}
283285

284286
It "Should find resource given specific Name, Version null" {
285-
$res = Find-PSResource -Name "Az.Accounts" -Repository "MAR"
287+
$res = Find-PSResource -Name "Az.Accounts" -Repository $MarName
286288
$res.Name | Should -Be "Az.Accounts"
287289
$res.Version | Should -BeGreaterThan ([Version]"4.0.0")
288290
}
289291

290292
It "Should find resource and its dependency given specific Name and Version" {
291-
$res = Find-PSResource -Name "Az.Storage" -Version "8.0.0" -Repository "MAR"
293+
$res = Find-PSResource -Name "Az.Storage" -Version "8.0.0" -Repository $MarName
292294
$res.Dependencies.Length | Should -Be 1
293295
$res.Dependencies[0].Name | Should -Be "Az.Accounts"
294296
}
295297

296298
It "Should find Azpreview resource and it's dependency given specific Name and Version" {
297-
$res = Find-PSResource -Name "Azpreview" -Version "13.2.0" -Repository "MAR"
299+
$res = Find-PSResource -Name "Azpreview" -Version "13.2.0" -Repository $MarName
298300
$res.Dependencies.Length | Should -Not -Be 0
299301
}
300302

301303
It "Should find resource with wildcard in Name" {
302-
$res = Find-PSResource -Name "Az.App*" -Repository "MAR"
304+
$res = Find-PSResource -Name "Az.App*" -Repository $MarName
303305
$res | Should -Not -BeNullOrEmpty
304306
$res.Count | Should -BeGreaterThan 1
305307
}
306308

307309
It "Should find all resource with wildcard in Name" {
308-
$res = Find-PSResource -Name "*" -Repository "MAR"
310+
$res = Find-PSResource -Name "*" -Repository $MarName
309311
$res | Should -Not -BeNullOrEmpty
310312
$res.Count | Should -BeGreaterThan 1
311313
}
312314

313315
It "Should find version range for Az dependencies" {
314316
# Target known version to know the output from the API won't change
315-
$res = Find-PSResource -Repository 'MAR' -Name 'Az' -Version '14.4.0'
316-
317+
$res = Find-PSResource -Repository $MarName -Name 'Az' -Version '14.4.0'
318+
317319
# Version defined by "ModuleVersion"
318320
$res.Dependencies.Where{$_.'Name' -eq 'Az.Accounts'}.'VersionRange'.ToString() | Should -Be '[5.3.0, )'
319-
321+
320322
# Version defined by "RequiredVersion"
321323
$res.Dependencies.Where{$_.'Name' -eq 'Az.Resources'}.'VersionRange'.ToString() | Should -Be '[8.1.0, 8.1.0]'
322324
}

test/InstallPSResourceTests/InstallPSResourceContainerRegistryServer.Tests.ps1

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -351,25 +351,24 @@ Describe 'Test Install-PSResource for Container Registry scenarios - Manual Vali
351351
}
352352

353353
Describe 'Test Install-PSResource for MAR Repository' -tags 'CI' {
354+
354355
BeforeAll {
355-
[Microsoft.PowerShell.PSResourceGet.UtilClasses.InternalHooks]::SetTestHook("MARPrefix", "azure-powershell/");
356-
Register-PSResourceRepository -Name "MAR" -Uri "https://mcr.microsoft.com" -ApiVersion "ContainerRegistry"
356+
Get-NewPSResourceRepositoryFile
357357
}
358-
359358
AfterAll {
360-
[Microsoft.PowerShell.PSResourceGet.UtilClasses.InternalHooks]::SetTestHook("MARPrefix", $null);
361-
Unregister-PSResourceRepository -Name "MAR"
359+
Get-RevertPSResourceRepositoryFile
362360
}
363361

364-
It "Should find resource given specific Name, Version null" {
362+
It "Should install resource given specific Name, Version null" {
365363
try {
366-
$pkg = Install-PSResource -Name "Az.Accounts" -Repository "MAR" -PassThru -TrustRepository -Reinstall
364+
$pkg = Install-PSResource -Name "Az.Accounts" -Repository 'MicrosoftArtifactRegistry' -PassThru -TrustRepository -Reinstall
367365
$pkg.Name | Should -Be "Az.Accounts"
368-
$pkg.Version | Should -Be "3.0.4"
366+
$pkg.Version.Major | Should -BeGreaterOrEqual 5
367+
369368
}
370369
finally {
371370
if ($pkg) {
372-
Uninstall-PSResource -Name "Az.Accounts" -Version "3.0.4"
371+
Uninstall-PSResource -Name "Az.Accounts" -Version $pkg.Version
373372
}
374373
}
375374
}

0 commit comments

Comments
 (0)