func()

in pkg/infrastructure/authorizationCheckers/ARMTemplateWhatIf/armTemplateWhatIfAuthorizationChecker.go [130:262]


func (a *armWhatIfConfig) GetARMWhatIfAuthorizationErrors(deploymentName string, mpfConfig domain.MPFConfig) (string, error) {

	bearerToken, err := a.azAPIClient.GetSPBearerToken(mpfConfig.TenantID, mpfConfig.SP.SPClientID, mpfConfig.SP.SPClientSecret)
	if err != nil {
		// wrap error and return
		return "", fmt.Errorf("error getting bearer token: %w", err)
	}

	// read template and parameters
	template, err := mpfSharedUtils.ReadJson(a.armConfig.TemplateFilePath)
	if err != nil {
		// log.Errorf("Error reading template file: %v", err)
		return "", fmt.Errorf("%w: %w", ARMTemplateShared.ErrInvalidTemplate, fmt.Errorf("error reading template file: %w", err))
	}

	parameters, err := mpfSharedUtils.ReadJson(a.armConfig.ParametersFilePath)
	if err != nil {
		return "", fmt.Errorf("%w: %w", ARMTemplateShared.ErrInvalidTemplate, fmt.Errorf("error reading parameters file: %w", err))
	}

	// convert parameters to standard format
	parameters = ARMTemplateShared.GetParametersInStandardFormat(parameters)

	// if Subscription scoped, we need to specify deployment location

	fullTemplate := map[string]interface{}{
		"properties": map[string]interface{}{
			"mode":       "Incremental",
			"template":   template,
			"parameters": parameters,
		},
	}

	if a.armConfig.SubscriptionScoped {
		fullTemplate["location"] = a.armConfig.Location
	}

	// convert bodyJSON to string
	fullTemplateJSONBytes, err := json.Marshal(fullTemplate)
	if err != nil {
		return "", fmt.Errorf("%w, %w", ARMTemplateShared.ErrInvalidTemplate, fmt.Errorf("error marshalling fullTemplateJSON: %w", err))
	}

	fullTemplateJSONString := string(fullTemplateJSONBytes)

	log.Debugln()
	log.Debugln(fullTemplateJSONString)
	log.Debugln()
	// create JSON body with template and parameters

	client := &http.Client{}

	var url string
	if a.armConfig.SubscriptionScoped {
		url = fmt.Sprintf("https://management.azure.com/subscriptions/%s/providers/Microsoft.Resources/deployments/%s/whatIf?api-version=2022-09-01", mpfConfig.SubscriptionID, deploymentName)
	} else {
		url = fmt.Sprintf("https://management.azure.com/subscriptions/%s/resourcegroups/%s/providers/Microsoft.Resources/deployments/%s/whatIf?api-version=2021-04-01", mpfConfig.SubscriptionID, mpfConfig.ResourceGroup.ResourceGroupName, deploymentName)
	}

	reqMethod := "POST"

	req, err := http.NewRequest(reqMethod, url, bytes.NewBufferString(fullTemplateJSONString))
	if err != nil {
		return "", err
	}

	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Accept", "application/json")
	req.Header.Set("User-Agent", "Go HTTP Client")

	// add bearer token to header
	req.Header.Add("Authorization", "Bearer "+bearerToken)

	resp, err := client.Do(req)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()

	bodyBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	log.Debugf("Response Body: %s", string(bodyBytes))

	// if response status code is 400, indicates invalid template
	if resp.StatusCode == 400 {
		// log.Errorf("InvalidTemplate error occured: %s. Please check the Template and Parameters", resp.Status)
		// return "", errors.New("InvalidTemplate")
		// return "", fmt.Error("InvalidTemplate: Please check the Template and Parameters: %w", resp.Status)
		log.Warnf("Response Body: %s", string(bodyBytes))
		return "", fmt.Errorf("%w, %w", ARMTemplateShared.ErrInvalidTemplate, errors.New("please check the template and parameter files"))
	}

	whatIfRespLoc := resp.Header.Get("Location")
	log.Debugf("What if response Location: %s \n", whatIfRespLoc)

	_, err = URL.ParseRequestURI(whatIfRespLoc)
	if err != nil {
		// return "", err
		log.Warnf("Response Body: %s", string(bodyBytes))
		return "", fmt.Errorf("Error parsing what if response location: %w", err)
	}

	respBody, err := a.GetWhatIfResp(whatIfRespLoc, bearerToken)
	if err != nil {
		log.Infof("Could not fetch what if response: %v \n", err)
		// return "", err
		return "", fmt.Errorf("Could not fetch what if response: %w", err)
	}

	log.Debugln(respBody)

	switch {
	case strings.Contains(respBody, "InvalidTemplate") && !strings.Contains(respBody, "InvalidTemplateDeployment"):
		// This indicates the ARM Template or Bicep File has issues.
		// Sample
		// {"status":"Failed","error":{"code":"InvalidTemplate","message":"Deployment template validation failed: 'The template parameters 'aksClusterName, virtualNetworkName' in the parameters file are not valid; they are not present in the original template and can therefore not be provided at deployment time. The only supported parameters for this template are 'clusterName, location, subnetName, vnetName'. Please see https://aka.ms/arm-pass-parameter-values for usage details.'.","additionalInfo":[{"type":"TemplateViolation","info":{"lineNumber":0,"linePosition":0,"path":""}}]}}
		return "", fmt.Errorf("%w: please check the template and parameters file: %s", ARMTemplateShared.ErrInvalidTemplate, respBody)
	case strings.Contains(respBody, "Authorization") && !strings.Contains(respBody, "{\"status\":\"Succeeded\""):
		// This indicates Authorization errors occured
		return respBody, nil
	case strings.Contains(respBody, "InvalidTemplateDeployment"):
		// This indicates all Authorization errors are fixed
		// Sample error [{\"code\":\"PodIdentityAddonFeatureFlagNotEnabled\",\"message\":\"Provisioning of resource(s) for container service aks-24xalwx7i2ueg in resource group testdeployrg-Y2jsRAG failed. Message: PodIdentity addon is not allowed since feature 'Microsoft.ContainerService/EnablePodIdentityPreview' is not enabled.
		// Hence ok to proceed, and not return error in this condition
		log.Warnf("Post Authorizaton error occured: %s", respBody)

	}

	return "", nil

}