Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Set-AzIoTDeviceProvisioningServiceCertificate incorrectly passes the certificate as the query parameter 'certificate.name' instead of the body's member certificate.rawBytes #26669

Open
robinmholt opened this issue Nov 12, 2024 · 4 comments
Labels
bug This issue requires a change to an existing behavior in the product in order to be resolved. customer-reported needs-triage This is a new issue that needs to be triaged to the appropriate team.

Comments

@robinmholt
Copy link

Description

When I invoke:

Set-AzIoTDeviceProvisioningServiceCertificate -ResourceGroupName MyResourceGroup  -Name MyDPS -CertificateName $certName -Etag $azDPSValidationCode.Etag -Path "${smCertName}-validation-cert.pem"

I see the Restful API call is taking the body of the certificate and assigning it to the certificate.name query parameter.

The following was referencing git commit 174cc61.

Working back from the code which assembles the query string, I see:
In DeviceProvisioningServices.Management.Sdk/Generated/DpsCertificateOperations.cs:

1312         public async Task<AzureOperationResponse<CertificateResponse>> VerifyCertificateWithHttpMessagesAsync(string certificateName, string ifMatch, string resourceGroupName, string provisioningServiceName, string certificatename = default(string), byte[] certificaterawBytes = default(byte[]), bool? certificateisVerified = default(bool?), string certificatepurpose = default(string), System.DateTime? certificatecreated = default(System.DateTime?), System.DateTime? certificatelastUpdated = default(System.DateTime?), bool? certificatehasPrivateKey = default(bool?), string certificatenonce = default(string), string certificate = default(string), Dictionary<string, List<string>> customHeaders = null, CancellationToken cancellationToken = default(CancellationToken))
...
1374             if (certificatename != null)
1375             {
1376                 _queryParameters.Add(string.Format("certificate.name={0}", System.Uri.EscapeDataString(certificatename)));
1377             }
1378             if (certificaterawBytes != null)
1379             {
1380                 _queryParameters.Add(string.Format("certificate.rawBytes={0}", System.Uri.EscapeDataString(Rest.Serialization.SafeJsonConvert.SerializeObject(certificaterawBytes, Client.SerializationSettings).Trim('"'))));
1381             }

This tells me that the passed in parameter 5 (certificatename), not to be confused with the parameter 1 (certificateName), is being passed in as a string. What I intend to do is pass in parameter 6 (certificaterawBytes) as the body of my certificate.

Tracking back to the call location, I see:
DeviceProvisioningServices.Management.Sdk/Generated/DpsCertificateOperationsExtensions.cs:

449             public static CertificateResponse VerifyCertificate(this IDpsCertificateOperations operations, string certificateName, string ifMatch, string resourceGroupName, string provisioningServiceName, string certificatename = default(string), byte[] certificaterawBytes = default(byte[]), bool? certificateisVerified = default(bool?), string certificatepurpose = default(string), System.DateTime? certificatecreated = default(System.DateTime?), System.DateTime? certificatelastUpdated = default(System.DateTime?), bool? certificatehasPrivateKey = default(bool?), string certificatenonce = default(string), string certificate = default(string))
450             {
451                 return operations.VerifyCertificateAsync(certificateName, ifMatch, resourceGroupName, provisioningServiceName, certificatename, certificaterawBytes, certificateisVerified, certificatepurpose, certificatecreated, certificatelastUpdated, certificatehasPrivateKey, certificatenonce, certificate).GetAwaiter().GetResult();
452             }
...

509             public static async Task<CertificateResponse> VerifyCertificateAsync(this IDpsCertificateOperations operations, string certificateName, string ifMatch, string resourceGroupName, string provisioningServiceName, string certificatename = default(string), byte[] certificaterawBytes = default(byte[]), bool? certificateisVerified = default(bool?), string certificatepurpose = default(string), System.DateTime? certificatecreated = default(System.DateTime?), System.DateTime? certificatelastUpdated = default(System.DateTime?), bool? certificatehasPrivateKey = default(bool?), string certificatenonce = default(string), string certificate = default(string), CancellationToken cancellationToken = default(CancellationToken))
510             {
511                 using (var _result = await operations.VerifyCertificateWithHttpMessagesAsync(certificateName, ifMatch, resourceGroupName, provisioningServiceName, certificatename, certificaterawBytes, certificateisVerified, certificatepurpose, certificatecreated, certificatelastUpdated, certificatehasPrivateKey, certificatenonce, certificate, null, cancellationToken).ConfigureAwait(false))
512                 {
513                     return _result.Body;
514                 }
515             }
516
517     }
518 }

That, in turn is called from:
Cmdlet/IotDpsCertificates/SetAzureRmIotDeviceProvisioningServiceCertificate.cs

162             certificate = Encoding.UTF8.GetString(Encoding.UTF8.GetBytes(certificate));
163
164             VerificationCodeRequest verificationCodeRequest = new VerificationCodeRequest();
165             verificationCodeRequest.Certificate = certificate;
166
167             CertificateResponse certificateResponse = this.IotDpsClient.DpsCertificate.VerifyCertificate(this.CertificateName, this.Etag,this.ResourceGroupName, this.Name, verificationCodeRequest.Certificate);
168             this.WriteObject(IotDpsUtils.ToPSCertificateResponse(certificateResponse));

And just for completness:
DeviceProvisioningServices.Management.Sdk/Generated/Models/VerificationCodeRequest.cs

 19     public partial class VerificationCodeRequest
 20     {
...
 49         [JsonProperty(PropertyName = "certificate")]
 50         public string Certificate { get; set; }

Conclusion:

No matter what I pass to the Set-AzIoTDeviceProvisioningServiceCertificate command, it will be converted to a string in SetAzureRmIoTDeviceProvisioningServiceCertificate by lines 162-167.

That, in-turn will cause VerifyCertificate to receive what I am passing as a certificate in the certificatename parameter, which is in-turn passed to VerifyCertificateAsync as the certificatename parameter, which in turn is passed to VerifyCertificateWithHttpMessagesAsync as its certificatename parameter, resulting in the wrong query string.

Issue script & Debug output

PS /home/holtr/src/SystemManager/Certificate_Signing_Service/Powershell_scripts> $resp = Set-AzIoTDeviceProvisioningServiceCertificate -ResourceGroupName IoTHub-cu-ft-rg  -Name Telemetry-cu-ft-dps -CertificateName $smCertName -Etag $azDPSValidationCode.Etag -Path "${smCertName}-validation-cert.pem"
DEBUG: 2:31:28 PM - [ConfigManager] Got nothing from [DisplaySecretsWarning], Module = [], Cmdlet = []. Returning default value [True].
DEBUG: 2:31:28 PM - SetAzureRmIoTDeviceProvisioningServiceCertificate begin processing with ParameterSet 'ResourceSet'.
DEBUG: 2:31:28 PM - using account id '[email protected]'...
DEBUG: 2:31:28 PM - [ConfigManager] Got nothing from [DisplayBreakingChangeWarning], Module = [], Cmdlet = []. Returning default value [True].
DEBUG: [Common.Authentication]: Authenticating using Account: '[email protected]', environment: 'AzureCloud', tenant: 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX'
DEBUG: 2:31:28 PM - [ConfigManager] Got nothing from [DisableInstanceDiscovery], Module = [], Cmdlet = []. Returning default value [False].
DEBUG: 2:31:28 PM - [ConfigManager] Got [False] from [EnableLoginByWam], Module = [], Cmdlet = [].
DEBUG: 2:31:28 PM - [SilentAuthenticator] Calling SharedTokenCacheCredential.GetTokenAsync - TenantId:'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', Scopes:'https://management.core.windows.net//.default', AuthorityHost:'https://login.microsoftonline.com/', UserId:'[email protected]'
DEBUG: SharedTokenCacheCredential.GetToken invoked. Scopes: [ https://management.core.windows.net//.default ] ParentRequestId:
DEBUG: False MSAL 4.65.0.0 MSAL.CoreCLR .NET 8.0.10 Linux [2024-11-12 14:31:28Z - 0248ebf2-4f2e-4980-bf8f-b2a3f899b24a] IsLegacyAdalCacheEnabled: yes
DEBUG: False MSAL 4.65.0.0 MSAL.CoreCLR .NET 8.0.10 Linux [2024-11-12 14:31:28Z - 0248ebf2-4f2e-4980-bf8f-b2a3f899b24a] [Region discovery] Not using a regional authority.
DEBUG: False MSAL 4.65.0.0 MSAL.CoreCLR .NET 8.0.10 Linux [2024-11-12 14:31:28Z - 0248ebf2-4f2e-4980-bf8f-b2a3f899b24a] [Region discovery] Not using a regional authority.
DEBUG: False MSAL 4.65.0.0 MSAL.CoreCLR .NET 8.0.10 Linux [2024-11-12 14:31:28Z - 0248ebf2-4f2e-4980-bf8f-b2a3f899b24a] IsLegacyAdalCacheEnabled: yes
DEBUG: False MSAL 4.65.0.0 MSAL.CoreCLR .NET 8.0.10 Linux [2024-11-12 14:31:28Z - 0248ebf2-4f2e-4980-bf8f-b2a3f899b24a] IsLegacyAdalCacheEnabled: yes
DEBUG: False MSAL 4.65.0.0 MSAL.CoreCLR .NET 8.0.10 Linux [2024-11-12 14:31:28Z] Found 1 cache accounts and 0 broker accounts
DEBUG: False MSAL 4.65.0.0 MSAL.CoreCLR .NET 8.0.10 Linux [2024-11-12 14:31:28Z] Returning 1 accounts
DEBUG: False MSAL 4.65.0.0 MSAL.CoreCLR .NET 8.0.10 Linux [2024-11-12 14:31:28Z - e30ded8c-25da-44e3-bb18-49133209dc35] MSAL MSAL.CoreCLR with assembly version '4.65.0.0'. CorrelationId(e30ded8c-25da-44e3-bb18-49133209dc35)
DEBUG: False MSAL 4.65.0.0 MSAL.CoreCLR .NET 8.0.10 Linux [2024-11-12 14:31:28Z - e30ded8c-25da-44e3-bb18-49133209dc35] === AcquireTokenSilent Parameters ===
DEBUG: False MSAL 4.65.0.0 MSAL.CoreCLR .NET 8.0.10 Linux [2024-11-12 14:31:28Z - e30ded8c-25da-44e3-bb18-49133209dc35] LoginHint provided: False
DEBUG: False MSAL 4.65.0.0 MSAL.CoreCLR .NET 8.0.10 Linux [2024-11-12 14:31:28Z - e30ded8c-25da-44e3-bb18-49133209dc35] Account provided: True
DEBUG: False MSAL 4.65.0.0 MSAL.CoreCLR .NET 8.0.10 Linux [2024-11-12 14:31:28Z - e30ded8c-25da-44e3-bb18-49133209dc35] ForceRefresh: False
DEBUG: False MSAL 4.65.0.0 MSAL.CoreCLR .NET 8.0.10 Linux [2024-11-12 14:31:28Z - e30ded8c-25da-44e3-bb18-49133209dc35]
=== Request Data ===
Authority Provided? - True
Scopes - https://management.core.windows.net//.default
Extra Query Params Keys (space separated) -
ApiId - AcquireTokenSilent
IsConfidentialClient - False
SendX5C - False
LoginHint ? False
IsBrokerConfigured - False
HomeAccountId - False
CorrelationId - e30ded8c-25da-44e3-bb18-49133209dc35
UserAssertion set: False
LongRunningOboCacheKey set: False
Region configured:

DEBUG: False MSAL 4.65.0.0 MSAL.CoreCLR .NET 8.0.10 Linux [2024-11-12 14:31:28Z - e30ded8c-25da-44e3-bb18-49133209dc35] === Token Acquisition (SilentRequest) started:
         Scopes: https://management.core.windows.net//.default
        Authority Host: login.microsoftonline.com
DEBUG: False MSAL 4.65.0.0 MSAL.CoreCLR .NET 8.0.10 Linux [2024-11-12 14:31:28Z - e30ded8c-25da-44e3-bb18-49133209dc35] [Region discovery] Not using a regional authority.
DEBUG: False MSAL 4.65.0.0 MSAL.CoreCLR .NET 8.0.10 Linux [2024-11-12 14:31:28Z - e30ded8c-25da-44e3-bb18-49133209dc35] Access token is not expired. Returning the found cache entry. [Current time (11/12/2024 14:31:28) - Expiration Time (11/12/2024 15:16:45 +00:00) - Extended Expiration Time (11/12/2024 15:16:45 +00:00)]
DEBUG: False MSAL 4.65.0.0 MSAL.CoreCLR .NET 8.0.10 Linux [2024-11-12 14:31:28Z - e30ded8c-25da-44e3-bb18-49133209dc35] Returning access token found in cache. RefreshOn exists ? False
DEBUG: False MSAL 4.65.0.0 MSAL.CoreCLR .NET 8.0.10 Linux [2024-11-12 14:31:28Z - e30ded8c-25da-44e3-bb18-49133209dc35] [Region discovery] Not using a regional authority.
DEBUG: False MSAL 4.65.0.0 MSAL.CoreCLR .NET 8.0.10 Linux [2024-11-12 14:31:28Z - e30ded8c-25da-44e3-bb18-49133209dc35]
        === Token Acquisition finished successfully:
DEBUG: False MSAL 4.65.0.0 MSAL.CoreCLR .NET 8.0.10 Linux [2024-11-12 14:31:28Z - e30ded8c-25da-44e3-bb18-49133209dc35]  AT expiration time: 11/12/2024 3:16:45 PM +00:00, scopes: https://management.core.windows.net//.default https://management.core.windows.net//user_impersonation. source: Cache
DEBUG: SharedTokenCacheCredential.GetToken succeeded. Scopes: [ https://management.core.windows.net//.default ] ParentRequestId:  ExpiresOn: 2024-11-12T15:16:45.0000000+00:00
DEBUG: [Common.Authentication]: Received token with LoginType 'User', Tenant: 'XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX', UserId: '[email protected]'
DEBUG: ============================ HTTP REQUEST ============================

HTTP Method:
POST

Absolute Uri:
https://management.azure.com/subscriptions/XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX/resourceGroups/IoTHub-cu-ft-rg/providers/Microsoft.Devices/provisioningServices/Telemetry-cu-ft-dps/certificates/SM-DPS-20241110-20441112/verify?certificate.name=-----BEGIN CERTIFICATE-----%0AMIIC1zCCAb%2BgAwIBAgIBATANBgkqhkiG9w0BAQsFADAjMSEwHwYDVQQDExhTTS1EUFMtMjAyNDEx%0D%0AMTAtMjA0NDExMTIwHhcNMjQxMTEyMDAwMDAwWhcNMjQxMTE5MDAwMDAwWjA7MTkwNwYDVQQDEzBD%0D%0AMkY4OEVFNDUwMzYxMzk3NzkzRkM1NDY4M0Q2M0ZGRDMxQ0NEQTM1MDhFRjZEODcwggEiMA0GCSqG%0D%0ASIb3DQEBAQUAA4IBDwAwggEKAoIBAQDXHIOxXEnqur9xTof743j4i%2F74vG%2FZKVx070dNVmeH%2BomQ%0D%0AfRpgzJQsOh%2Fvmwrg%2FUZCpUzGclGbNNPJwuzdffNSNb%2Bpfc%2FZcjdv75lp8iPMtv%2BMFvH3csfgJ2e1%0D%0Acu1qNt70oIoc8Kfn%2FNDyEpsYtQCn3IfA4Rzt5VWanlt4pTZ3exgblGjdzkVTbiTu%2F1sUM%2F3ONlPX%0D%0A7XUvu6PtJrJ7aJ%2BkjRUOQgZzuenvaMlQszGIIzF2kILo2oNI95%2F7dgwClMA385O6YKpaVEeK3WVO%0D%0AMMZQj29F5c3uNCrrelcKMVMHwvbbdUO2JZoyszz9OetHH6z9U%2F5hkAOUP1hJ3HQBdw85AgMBAAEw%0D%0ADQYJKoZIhvcNAQELBQADggEBADzCwtYGX3D7JvXouSf6OOjN0bmz7ZM2XFX6YRhtLTeeMsEgu8UY%0D%0AKoGWxcMoXu9uxuw3MZCso%2BRuJDvkMUHo%2Ff5xhYLYcwA31mfGVYS2DEdjIzIgUktjNu6masju63pS%0D%0Aet%2BAhf4Tl8NasxEvJVAzDvg52kz%2FchlDOEaSd9slCsxfqnVVlrwJx5aioBN3DS4LlIX940AQeV0D%0D%0A7sNcunqP6ypPTXtna5IHILCxTs7X0%2BrygieubeBQ5nN%2FJeyleEWr35yc%2FK2xm8H6XS%2BD5JRRLQU6%0D%0APhzuOiz1pOrlqDEnnjSSMVS93fLxy0zVQ9kkHOSfPaBNuyaSA4wN55oN3iaEXeQ%3D%0A-----END CERTIFICATE-----%0A%0A&api-version=2017-11-15

Headers:
If-Match                      : ImIyMDBjZWMxLTAwMDAtMDMwMC0wMDAwLTY3MzM2NjU2MDAwMCI=
Accept-Language               : en-US
x-ms-client-request-id        : cb29fad8-aa2f-4575-8941-62ad752ade91

Body:
{}


DEBUG: ============================ HTTP RESPONSE ============================

Status Code:
BadRequest

Headers:
Cache-Control                 : no-cache
Pragma                        : no-cache
x-ms-ratelimit-remaining-subscription-writes: 199
x-ms-ratelimit-remaining-subscription-global-writes: 2999
x-ms-request-id               : c8712f18-68d6-4a4a-94f2-fdcece22b58c
x-ms-correlation-request-id   : c8712f18-68d6-4a4a-94f2-fdcece22b58c
x-ms-routing-request-id       : NORTHCENTRALUS:20241112T143129Z:c8712f18-68d6-4a4a-94f2-fdcece22b58c
Strict-Transport-Security     : max-age=31536000; includeSubDomains
X-Content-Type-Options        : nosniff
X-Cache                       : CONFIG_NOCACHE
X-MSEdge-Ref                  : Ref A: 0E4270D18E81464288C136D004CDE333 Ref B: CH1AA2020614025 Ref C: 2024-11-12T14:31:28Z
Date                          : Tue, 12 Nov 2024 14:31:28 GMT

Body:
{
  "code": 400059,
  "httpStatusCode": "BadRequest",
  "message": "Request body validation failed. If you contact a support representative please include this correlation identifier: ae22b3b4-2fed-49dc-a85e-71b0da2eb7b6, timestamp: 2024-11-12 14:31:29Z, errorcode: IH400059."
}


DEBUG: 2:31:29 PM - [ConfigManager] Got nothing from [EnableErrorRecordsPersistence], Module = [], Cmdlet = []. Returning default value [False].
Set-AzIoTDeviceProvisioningServiceCertificate: Operation returned an invalid status code 'BadRequest'
DEBUG: 2:31:29 PM - [ConfigManager] Got nothing from [DisplayBreakingChangeWarning], Module = [], Cmdlet = []. Returning default value [True].
DEBUG: 2:31:29 PM - [ConfigManager] Got nothing from [DisplayRegionIdentified], Module = [], Cmdlet = []. Returning default value [True].
DEBUG: 2:31:29 PM - [ConfigManager] Got nothing from [CheckForUpgrade], Module = [], Cmdlet = []. Returning default value [True].
DEBUG: AzureQoSEvent:  Module: Az.DeviceProvisioningServices:0.10.3; CommandName: Set-AzIoTDeviceProvisioningServiceCertificate; PSVersion: 7.4.6; IsSuccess: False; Duration: 00:00:01.0126382; SanitizeDuration: 00:00:00; Exception: Operation returned an invalid status code 'BadRequest';
DEBUG: 2:31:29 PM - [ConfigManager] Got nothing from [EnableDataCollection], Module = [], Cmdlet = []. Returning default value [True].
DEBUG: 2:31:29 PM - SetAzureRmIoTDeviceProvisioningServiceCertificate end processing.
PS /home/holtr/src/SystemManager/Certificate_Signing_Service/Powershell_scripts> $smCertName
SM-DPS-20241110-20441112

Environment data

Name                           Value
----                           -----
PSVersion                      7.4.6
PSEdition                      Core
GitCommitId                    7.4.6
OS                             CBL-Mariner/Linux
Platform                       Unix
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Module versions

ModuleType Version    PreRelease Name                                ExportedCommands
---------- -------    ---------- ----                                ----------------
Script     3.0.5                 Az.Accounts                         {Add-AzEnvironment, Clear-AzConfig, Clear-AzContext, Clear-AzDefault…}
Script     0.10.3                Az.DeviceProvisioningServices       {Add-AzIoTDeviceProvisioningServiceAccessPolicy, Add-AzIoTDeviceProvisioningServiceCertificate, Add-AzIoTDeviceProvisioningServiceEnrollment, Add-AzIoTDeviceProvisioningServiceEnrollmentGroup…}
Script     4.1.1                 Az.Functions                        {Get-AzFunctionApp, Get-AzFunctionAppAvailableLocation, Get-AzFunctionAppPlan, Get-AzFunctionAppSetting…}
Script     6.2.0                 Az.KeyVault                         {Add-AzKeyVaultCertificate, Add-AzKeyVaultCertificateContact, Add-AzKeyVaultKey, Add-AzKeyVaultManagedStorageAccount…}

Error output

@robinmholt robinmholt added bug This issue requires a change to an existing behavior in the product in order to be resolved. needs-triage This is a new issue that needs to be triaged to the appropriate team. labels Nov 12, 2024
@microsoft-github-policy-service microsoft-github-policy-service bot added customer-reported needs-triage This is a new issue that needs to be triaged to the appropriate team. and removed needs-triage This is a new issue that needs to be triaged to the appropriate team. labels Nov 12, 2024
@robinmholt
Copy link
Author

After playing with every setting I can think to adjust, I have come to the conclusion that there is no way to use this command as-is.

I attempted to upload the verification certificate and then pass that as the -Path <validation-code> and that prepends a file path so what is sent is the full directory path for the current directory appended with the string I specify.

@robinmholt
Copy link
Author

I have a change which compiles. I assume it will work, but am not at all familiar with Powershell and Powershell cmdlets so I am trying to figure out how to build and test these changes. I was able to do a dotnet build /t:Build /p:Configuration=Release;TurnOnTestCoverage=true;ModifiedModuleBuild=true from the src/DeviceProvisioningServices directory and that completed without any warning/error messages.

commit 9e49af2c02b84f3cab46a6ccca1b81e83ce866ea
Author: Robin Holt <[email protected]>
Date:   Wed Nov 13 09:51:54 2024 -0600

    Bug #26669: Adjust Set-IoTDeviceProvisioningServiceCertificate to name parameters, pass certificate raw bytes.
    
    The Set-IoTDeviceProvisioningServiceCertificate command currently wrongly
    passes the certificate provided to it as an option as the stringified
    version (either immediate, or by reading the path) which, because of
    positional parameters being used, gets passed as the certificatename
    parameter.  It really should be provided as the certificateRawBytes
    parameter.
    
    I also adjust the call to use named parameters for everything to reduce
    chances of this problem in the future.

diff --git a/src/DeviceProvisioningServices/DeviceProvisioningServices/Cmdlet/IotDpsCertificates/SetAzureRmIotDeviceProvisioningServiceCertificate.cs b/src/DeviceProvisioningServices/DeviceProvisioningServices/Cmdlet/IotDpsCertificates/SetAzureRmIotDeviceProvisioningServiceCertificate.cs
index 638512512e4..c1ebb0e220b 100644
--- a/src/DeviceProvisioningServices/DeviceProvisioningServices/Cmdlet/IotDpsCertificates/SetAzureRmIotDeviceProvisioningServiceCertificate.cs
+++ b/src/DeviceProvisioningServices/DeviceProvisioningServices/Cmdlet/IotDpsCertificates/SetAzureRmIotDeviceProvisioningServiceCertificate.cs
@@ -159,12 +159,15 @@ namespace Microsoft.Azure.Commands.Management.DeviceProvisioningServices
                     break;
             }
 
-            certificate = Encoding.UTF8.GetString(Encoding.UTF8.GetBytes(certificate));
-
-            VerificationCodeRequest verificationCodeRequest = new VerificationCodeRequest();
-            verificationCodeRequest.Certificate = certificate;
-
-            CertificateResponse certificateResponse = this.IotDpsClient.DpsCertificate.VerifyCertificate(this.CertificateName, this.Etag,this.ResourceGroupName, this.Name, verificationCodeRequest.Certificate);
+            byte[] certificaterawBytes = Encoding.UTF8.GetBytes(certificate);
+
+            CertificateResponse certificateResponse = this.IotDpsClient.DpsCertificate.VerifyCertificate(
+                certificateName: this.CertificateName,
+                ifMatch: this.Etag,
+                resourceGroupName: this.ResourceGroupName,
+                provisioningServiceName: this.Name,
+                certificaterawBytes: certificaterawBytes
+            );
             this.WriteObject(IotDpsUtils.ToPSCertificateResponse(certificateResponse));
         }
     }

@robinmholt
Copy link
Author

OK. That does not work.

I have a change which compiles. I assume it will work, but am not at all familiar with Powershell and Powershell cmdlets so I am trying to figure out how to build and test these changes. I was able to do a dotnet build /t:Build /p:Configuration=Release;TurnOnTestCoverage=true;ModifiedModuleBuild=true from the src/DeviceProvisioningServices directory and that completed without any warning/error messages.

commit 9e49af2c02b84f3cab46a6ccca1b81e83ce866ea
Author: Robin Holt <[email protected]>
Date:   Wed Nov 13 09:51:54 2024 -0600

    Bug #26669: Adjust Set-IoTDeviceProvisioningServiceCertificate to name parameters, pass certificate raw bytes.
    
    The Set-IoTDeviceProvisioningServiceCertificate command currently wrongly
    passes the certificate provided to it as an option as the stringified
    version (either immediate, or by reading the path) which, because of
    positional parameters being used, gets passed as the certificatename
    parameter.  It really should be provided as the certificateRawBytes
    parameter.
    
    I also adjust the call to use named parameters for everything to reduce
    chances of this problem in the future.

diff --git a/src/DeviceProvisioningServices/DeviceProvisioningServices/Cmdlet/IotDpsCertificates/SetAzureRmIotDeviceProvisioningServiceCertificate.cs b/src/DeviceProvisioningServices/DeviceProvisioningServices/Cmdlet/IotDpsCertificates/SetAzureRmIotDeviceProvisioningServiceCertificate.cs
index 638512512e4..c1ebb0e220b 100644
--- a/src/DeviceProvisioningServices/DeviceProvisioningServices/Cmdlet/IotDpsCertificates/SetAzureRmIotDeviceProvisioningServiceCertificate.cs
+++ b/src/DeviceProvisioningServices/DeviceProvisioningServices/Cmdlet/IotDpsCertificates/SetAzureRmIotDeviceProvisioningServiceCertificate.cs
@@ -159,12 +159,15 @@ namespace Microsoft.Azure.Commands.Management.DeviceProvisioningServices
                     break;
             }
 
-            certificate = Encoding.UTF8.GetString(Encoding.UTF8.GetBytes(certificate));
-
-            VerificationCodeRequest verificationCodeRequest = new VerificationCodeRequest();
-            verificationCodeRequest.Certificate = certificate;
-
-            CertificateResponse certificateResponse = this.IotDpsClient.DpsCertificate.VerifyCertificate(this.CertificateName, this.Etag,this.ResourceGroupName, this.Name, verificationCodeRequest.Certificate);
+            byte[] certificaterawBytes = Encoding.UTF8.GetBytes(certificate);
+
+            CertificateResponse certificateResponse = this.IotDpsClient.DpsCertificate.VerifyCertificate(
+                certificateName: this.CertificateName,
+                ifMatch: this.Etag,
+                resourceGroupName: this.ResourceGroupName,
+                provisioningServiceName: this.Name,
+                certificaterawBytes: certificaterawBytes
+            );
             this.WriteObject(IotDpsUtils.ToPSCertificateResponse(certificateResponse));
         }
     }

That does not work.

@robinmholt
Copy link
Author

I have not been able to get any restful calls without a body of { "certificate": "XXXX"} to work. Given that, I can not see how the generated code in DeviceProvisioningServices.Management.Sdk/Generated/DpsCertificateOperations.cs should be allowing the body to be specified, and I don't know how the generated code is actually generated.

My management has indicated that I have spent too much time on this already and we need to just use the restful api directly and abandon azure powershell.

That said, you still have a problem and the fact that you have not detected that you have a problem indicates your testing has a problem as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug This issue requires a change to an existing behavior in the product in order to be resolved. customer-reported needs-triage This is a new issue that needs to be triaged to the appropriate team.
Projects
None yet
Development

No branches or pull requests

1 participant