pulumi / pulumi-azuredevops

An AzureDevOps Pulumi resource package, providing multi-language access to AzureDevOps
Apache License 2.0
20 stars 3 forks source link

Can't create authorized Azure Container Repository service connection #122

Open mrcity opened 1 year ago

mrcity commented 1 year ago

Pulumi version: (local) 3.44.3 (server) unknown Pulumi ADO Library version: 2.5.0

I'm trying to use ServiceEndpointAzureEcr to set up a service connection between Azure DevOps & my Azure Container Registry. However, I've found the authorization parameter to be very poorly-documented online. My cloud support engineer found some relevant information as to what exactly this field should contain here: https://learn.microsoft.com/en-us/rest/api/azure/devops/serviceendpoint/endpoints/create?view=azure-devops-rest-7.0&tabs=HTTP#create-azure-rm-service-endpoint In short, the authorization parameters field should either contain "tenantid", "serviceprincipalid", "authenticationType", "serviceprincipalkey" or "username", "password". Then there is another field at the same level as parameters called scheme.

Nevertheless, I'm using Node.js and am trying the authorization parameter styled in the following way:

    authorization: {
      "parameters": `{"tenantid": "${vzterra.AADTenantId}", "serviceprincipalid": "${adoSP.principal.applicationId}", "authenticationType": "spnKey", "serviceprincipalkey": "${adoSP.password.value}"}`,
      "scheme": "ServicePrincipal"
    },

However, I see the following error message in Octopus:

azuredevops:index/serviceEndpointAzureEcr:ServiceEndpointAzureEcr resource 'ado-docker-service-connection' has a problem: expected type of "authorization" to be string. Examine values at 'ServiceEndpointAzureEcr.Authorization'.

A layman like me would believe this error message is telling me to make the whole authorization field a string, contrary to the documentation. Of course, changing the whole thing to a string as follows:

    authorization: `{
      "parameters": {"tenantid": "${vzterra.AADTenantId}", "serviceprincipalid": "${adoSP.principal.applicationId}", "authenticationType": "spnKey", "serviceprincipalkey": "${adoSP.password.value}"},
      "scheme": "ServicePrincipal"
    }`,

Yields this result from Octopus

error: Running program 'C:\Octopus\Work\20221205223720-766167-19350\StackPackageRef' failed with an unhandled exception:
TSError: Γ¿» Unable to compile TypeScript:
azure-services/ADOServiceConnection.ts(81,5): error TS2322: Type 'string' is not assignable to type '{ [key: string]: Input<string>; } | Promise<{ [key: string]: Input<string>; }> | OutputInstance<{ [key: string]: Input<string>; }> | undefined'.

Incidentally, keeping the parameters field as a JSON object returns a TypeScript compilation error as well, since it's expected to be a dictionary of strings.

To me, it seems like there could use some reconciliation between what the Azure API expects and what Pulumi expects. FWIW, I was able to make the following POST request to my Azure instance in the following manner:

POST https://dev.azure.com/myorganization/_apis/serviceendpoint/endpoints?api-version=7.0

{
  "data": {
    "subscriptionId": "some-uuid",
    "subscriptionName": "some-subscription-name",
    "environment": "AzureCloud",
    "scopeLevel": "Subscription",
    "creationMode": "Manual"
  },
  "name": "MyNewACRServiceEndpoint",
  "type": "dockerregistry",
  "url": "https://management.azure.com/",
  "authorization": {
    "parameters": {
      "tenantid": "some-aad-app-tenant-uuid",
      "serviceprincipalid": "some-aad-app-object-uuid",
      "authenticationType": "spnKey",
      "serviceprincipalkey": "some-password"
    },
    "scheme": "ServicePrincipal"
  },
  "isShared": false,
  "isReady": true,
  "serviceEndpointProjectReferences": [
    {
      "projectReference": {
        "id": "some-ado-project-id",
        "name": "some-ado-project-name"
      },
      "name": "MyNewACRServiceEndpoint"
    }
  ]
}

And it created the service connection just fine. Please have a look!

roothorp commented 1 year ago

Hi @mrcity, thanks for the issue! I've raised with the team so it can be prioritized and added to our work stack.

Morlakh commented 10 months ago

We're having same exact problem with C# SDK where Authorization is InputMap (dictionary), so not much wiggle room since it's strongly-typed.

ServiceEndpointAzureEcr is unusable in it's current state (or any Azure DevOps resource where Authorization object is involved).

error: azuredevops:index/serviceEndpointAzureEcr:ServiceEndpointAzureEcr resource 'myServiceEndpointAzureEcr connection' has a problem: expected type of "authorization" to be string. Examine values at 'myServiceEndpointAzureEcr connection.authorization'.

error: azuredevops:index/serviceEndpointDockerRegistry:ServiceEndpointDockerRegistry resource 'myServiceEndpointAzure' has a problem: expected type of "authorization" to be string. Examine values at 'myServiceEndpointAzure.authorization'.

iamdmitrij commented 10 months ago

From what I understand Authorization object/parameter is not supported by Microsoft's Azure DevOps Terraform: https://registry.terraform.io/providers/microsoft/azuredevops/latest/docs/resources/serviceendpoint_azurecr.

Azure DevOps authentication only works with PATs there (AZDO_PERSONAL_ACCESS_TOKEN env variable setting). And since this library uses the same Terraform provider, authorization object is really unused or ignored. So any Service Endpoints related to custom authorization wouldn't work with predefined principals here.

We have managed to solve this by using PATs and simply ignoring authorization parameter in Pulumi code. Then Terraform providers creates principals and service endpoint just fine. Keep in mind AZDO_PERSONAL_ACCESS_TOKEN PAT needs to have proper permissions to create Service Endpoints and PAT's user must be a valid user in Azure Entra ID.

Example in C#:

var azureConfig = Pulumi.AzureNative.Authorization.GetClientConfig.Invoke();
var azureContainerRegistryServiceEndpoint = new ServiceEndpointAzureEcr("test", new ServiceEndpointAzureEcrArgs
{
    ProjectId = "ProjectGuid",
    ServiceEndpointName = "test",
    AzurecrSubscriptionId = azureConfig.Apply(config => config.SubscriptionId),
    AzurecrSubscriptionName = "AzureSubscriptionName",
    AzurecrSpnTenantid = azureConfig.Apply(config => config.TenantId),
    ResourceGroup = resourceGroup.Name,
    AzurecrName = registry.Name,
});

Any plans to switch to native Azure DevOps API instead of Azure DevOps using Terraform provider?