Skip to content

Commit 458cec4

Browse files
authored
clean code (Azure#11)
* clean code * update
1 parent a144fe8 commit 458cec4

File tree

6 files changed

+131
-500
lines changed

6 files changed

+131
-500
lines changed

.github/CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@
156156

157157
# PRLabel: %area-Deploy
158158
/src/Areas/Deploy/ @qianwens @xiaofanzhou @Azure/azure-mcp
159+
/src/Areas/Quota/ @qianwens @xiaofanzhou @Azure/azure-mcp
159160

160161
# ServiceLabel: %area-Deploy
161162
# ServiceOwners: @qianwens @xiaofanzhou

src/Areas/Deploy/Commands/PlanGetCommand.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public override Task<CommandResponse> ExecuteAsync(CommandContext context, Parse
6868
return Task.FromResult(context.Response);
6969
}
7070

71-
var planTemplate = DeploymentPlanTemplateUtilV2.GetPlanTemplate(options.ProjectName, options.TargetAppService, options.ProvisioningTool, options.AzdIacOptions);
71+
var planTemplate = DeploymentPlanTemplateUtil.GetPlanTemplate(options.ProjectName, options.TargetAppService, options.ProvisioningTool, options.AzdIacOptions);
7272

7373
context.Response.Message = planTemplate;
7474
context.Response.Status = 200;

src/Areas/Deploy/Services/Util/DeploymentPlanTemplateUtil.cs

Lines changed: 122 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,23 @@
22
// Licensed under the MIT License.
33

44
using AzureMcp.Areas.Deploy.Models;
5+
using AzureMcp.Areas.Deploy.Models.Templates;
6+
using AzureMcp.Areas.Deploy.Services.Templates;
57

68
namespace AzureMcp.Areas.Deploy.Services.Util;
79

810
/// <summary>
9-
/// Utility class for generating deployment plan templates.
11+
/// Refactored utility class for generating deployment plan templates using embedded resources.
1012
/// </summary>
1113
public static class DeploymentPlanTemplateUtil
1214
{
1315
/// <summary>
14-
/// Generates a deployment plan template with the specified project name.
16+
/// Generates a deployment plan template using embedded template resources.
1517
/// </summary>
1618
/// <param name="projectName">The name of the project. Can be null or empty.</param>
19+
/// <param name="targetAppService">The target Azure service.</param>
20+
/// <param name="provisioningTool">The provisioning tool.</param>
21+
/// <param name="azdIacOptions">The Infrastructure as Code options for AZD.</param>
1722
/// <returns>A formatted deployment plan template string.</returns>
1823
public static string GetPlanTemplate(string projectName, string targetAppService, string provisioningTool, string? azdIacOptions = "")
1924
{
@@ -22,161 +27,147 @@ public static string GetPlanTemplate(string projectName, string targetAppService
2227
{
2328
azdIacOptions = "bicep";
2429
}
25-
var azureComputeHost = targetAppService.ToLowerInvariant() switch
30+
31+
DeploymentPlanTemplateParameters parameters = CreateTemplateParameters(projectName, targetAppService, provisioningTool, azdIacOptions);
32+
var executionSteps = GenerateExecutionSteps(parameters);
33+
34+
parameters.ExecutionSteps = executionSteps;
35+
36+
return TemplateService.ProcessTemplate("Plan/deployment-plan-base", parameters.ToDictionary());
37+
}
38+
39+
/// <summary>
40+
/// Creates template parameters from the provided inputs.
41+
/// </summary>
42+
private static DeploymentPlanTemplateParameters CreateTemplateParameters(
43+
string projectName,
44+
string targetAppService,
45+
string provisioningTool,
46+
string? azdIacOptions)
47+
{
48+
var azureComputeHost = GetAzureComputeHost(targetAppService);
49+
var title = string.IsNullOrWhiteSpace(projectName)
50+
? "Azure Deployment Plan"
51+
: $"Azure Deployment Plan for {projectName} Project";
52+
53+
return new DeploymentPlanTemplateParameters
54+
{
55+
Title = title,
56+
ProjectName = projectName,
57+
TargetAppService = targetAppService,
58+
ProvisioningTool = provisioningTool,
59+
IacType = azdIacOptions ?? "bicep",
60+
AzureComputeHost = azureComputeHost,
61+
};
62+
}
63+
64+
/// <summary>
65+
/// Gets the Azure compute host display name from the target app service.
66+
/// </summary>
67+
private static string GetAzureComputeHost(string targetAppService)
68+
{
69+
return targetAppService.ToLowerInvariant() switch
2670
{
2771
"containerapp" => "Azure Container Apps",
2872
"webapp" => "Azure Web App Service",
2973
"functionapp" => "Azure Functions",
3074
"aks" => "Azure Kubernetes Service",
3175
_ => "Azure Container Apps"
3276
};
77+
}
3378

34-
var aksDeploySteps = """
35-
2. Build and Deploy the Application
36-
1. Build and Push Docker Image: {Agent should check if Dockerfile exists, if not add the step: "generate a Dockerfile for the application deployment", if does, list the Dockerfile path}.
37-
2. Prepare Kubernetes Manifests: {Agent should check if Kubernetes YAML files exists, if not add the step: "generate for the application deployment", if does, list the yaml files path}.
38-
3. Deploy to AKS: Use `kubectl apply` to deploy manifests to the AKS cluster
39-
3. Validation:
40-
1. Verify pods are running and services are exposed
41-
""";
79+
/// <summary>
80+
/// Generates execution steps based on the deployment parameters.
81+
/// </summary>
82+
private static string GenerateExecutionSteps(DeploymentPlanTemplateParameters parameters)
83+
{
84+
var steps = new List<string>();
85+
var isAks = parameters.TargetAppService.ToLowerInvariant() == "aks";
4286

43-
var summary = "Summarize the deployment result and save to '.azure/summary.copilotmd'. It should list all changes deployment files and brief description of each file. Then have a diagram showing the provisioned azure resource.";
87+
if (parameters.ProvisioningTool.ToLowerInvariant() == "azd")
88+
{
89+
steps.AddRange(GenerateAzdSteps(parameters, isAks));
90+
}
91+
else if (parameters.ProvisioningTool.Equals(DeploymentTool.AzCli, StringComparison.OrdinalIgnoreCase))
92+
{
93+
steps.AddRange(GenerateAzCliSteps(parameters, isAks));
94+
}
95+
96+
return string.Join(Environment.NewLine, steps);
97+
}
98+
99+
/// <summary>
100+
/// Generates AZD-specific execution steps.
101+
/// </summary>
102+
private static List<string> GenerateAzdSteps(DeploymentPlanTemplateParameters parameters, bool isAks)
103+
{
44104
var steps = new List<string>();
105+
106+
var deployTitle = isAks ? "" : " And Deploy the Application";
107+
var checkLog = isAks ? "" : "6. Check the application log with tool `azd-app-log-get` to ensure the services are running.";
45108

46-
if (provisioningTool.ToLowerInvariant() == "azd")
109+
var azdStepReplacements = new Dictionary<string, string>
47110
{
48-
var deployTitle = targetAppService.ToLowerInvariant() == "aks"
49-
? ""
50-
: " And Deploy the Application";
51-
var checkLog = targetAppService.ToLowerInvariant() == "aks"
52-
? ""
53-
: "6. Check the application log with tool `azd-app-log-get` to ensure the services are running.";
54-
steps.Add($"""
55-
1. Provision Azure Infrastructure{deployTitle}:
56-
1. Based on following required Azure resources in plan, get the IaC rules from the tool `iac-rules-get`
57-
2. Generate IaC ({azdIacOptions} files) for required azure resources based on the plan.
58-
3. Pre-check: use `get_errors` tool to check generated Bicep grammar errors. Fix the errors if exist.
59-
4. Run the AZD command `azd up` to provision the resources and confirm each resource is created or already exists.
60-
5. Check the deployment output to ensure the resources are provisioned successfully.
61-
{checkLog}
62-
""");
63-
if (targetAppService.ToLowerInvariant() == "aks")
64-
{
65-
steps.Add(aksDeploySteps);
66-
steps.Add($$"""
67-
4: Summary:
68-
1. {{summary}}
69-
""");
70-
}
71-
else
72-
{
73-
steps.Add($$"""
74-
2: Summary:
75-
1. {{summary}}
76-
""");
77-
}
111+
{ "DeployTitle", deployTitle },
112+
{ "IacType", parameters.IacType },
113+
{ "CheckLog", checkLog }
114+
};
115+
116+
var azdSteps = TemplateService.ProcessTemplate("Plan/azd-steps", azdStepReplacements);
117+
steps.Add(azdSteps);
118+
119+
if (isAks)
120+
{
121+
steps.Add(TemplateService.LoadTemplate("Plan/aks-steps"));
122+
steps.Add(TemplateService.ProcessTemplate("Plan/summary-steps", new Dictionary<string, string> { { "StepNumber", "4" } }));
123+
}
124+
else
125+
{
126+
steps.Add(TemplateService.ProcessTemplate("Plan/summary-steps", new Dictionary<string, string> { { "StepNumber", "2" } }));
127+
}
128+
129+
return steps;
130+
}
131+
132+
/// <summary>
133+
/// Generates Azure CLI-specific execution steps.
134+
/// </summary>
135+
private static List<string> GenerateAzCliSteps(DeploymentPlanTemplateParameters parameters, bool isAks)
136+
{
137+
var steps = new List<string>();
78138

139+
steps.Add(TemplateService.LoadTemplate("Plan/azcli-steps"));
79140

141+
if (isAks)
142+
{
143+
steps.Add(TemplateService.LoadTemplate("Plan/aks-steps"));
80144
}
81-
else if (provisioningTool.Equals(DeploymentTool.AzCli, StringComparison.OrdinalIgnoreCase))
145+
else
82146
{
83-
steps.Add("""
84-
1. Provision Azure Infrastructure:
85-
1. Generate Azure CLI scripts for required azure resources based on the plan.
86-
2. Check and fix the generated Azure CLI scripts for grammar errors.
87-
3. Run the Azure CLI scripts to provision the resources and confirm each resource is created or already exists
88-
""");
89-
if (targetAppService.ToLowerInvariant() == "aks")
147+
var isContainerApp = parameters.TargetAppService.ToLowerInvariant() == "containerapp";
148+
if (isContainerApp)
90149
{
91-
steps.Add(aksDeploySteps);
150+
var containerAppReplacements = new Dictionary<string, string>
151+
{
152+
{ "AzureComputeHost", parameters.AzureComputeHost }
153+
};
154+
steps.Add(TemplateService.ProcessTemplate("Plan/containerapp-steps", containerAppReplacements));
92155
}
93156
else
94157
{
95-
var isContainerApp = targetAppService.ToLowerInvariant() == "containerapp";
96-
var containerAppOptions = isContainerApp ? " 1. Build and Push Docker Image: Agent should check if Dockerfile exists, if not add the step: 'generate a Dockerfile for the application deployment', if it does, list the Dockerfile path" : "";
97-
var orderList = isContainerApp ? "2." : "1.";
98-
steps.Add($$"""
158+
// For other app services, generate basic deployment steps
159+
var basicSteps = $"""
99160
2. Build and Deploy the Application:
100-
{{containerAppOptions}}
101-
{{orderList}} Deploy to {{azureComputeHost}}: Use Azure CLI command to deploy the application
161+
1. Deploy to {parameters.AzureComputeHost}: Use Azure CLI command to deploy the application
102162
3. Validation:
103163
1. Verify command output to ensure the application is deployed successfully
104-
""");
164+
""";
165+
steps.Add(basicSteps);
105166
}
106-
steps.Add($$"""
107-
4: Summary:
108-
1 {{summary}}
109-
""");
110167
}
111-
var title = string.IsNullOrWhiteSpace(projectName)
112-
? "Azure Deployment Plan"
113-
: $"Azure Deployment Plan for {projectName} Project";
114-
115-
return $$"""
116-
{Agent should fill in and polish the markdown template below to generate a deployment plan for the project. Then save it to '.azure/plan.copilotmd' file. Don't add cost estimation! Don't add extra validation steps unless it is required! Don't change the tool name!}
117-
118-
#Title: {{title}}
119-
## **Goal**
120-
Based on the project to provide a plan to deploy the project to Azure using AZD. It will generate Bicep files and Azure YAML configuration.
121-
122-
123-
## **Project Information**
124-
{
125-
briefly summarize the project structure, services, and configurations, example:
126-
AppName: web
127-
- **Technology Stack**: ASP.NET Core 7.0 Razor Pages application
128-
- **Application Type**: Task Manager web application with client-side JavaScript
129-
- **Containerization**: Ready for deployment with existing Dockerfile
130-
- **Dependencies**: No external dependencies detected (database, APIs, etc.)
131-
- **Hosting Recommendation**: Azure Container Apps for scalable, serverless container hosting
132-
}
133-
134-
## **Azure Resources Architecture**
135-
> **Install the mermaid extension in IDE to view the architecture.**
136-
{a mermaid graph of following recommended azure resource architecture. Only keep the most important edges to make structure clear and readable.}
137-
{
138-
List how data flows between the services, example:
139-
- The container app gets its image from the Azure Container Registry.
140-
- The container app gets requests and interacts with the Azure SQL Database for data storage and retrieval.
141-
}
142168

169+
steps.Add(TemplateService.ProcessTemplate("Plan/summary-steps", new Dictionary<string, string> { { "StepNumber", "4" } }));
143170

144-
## **Recommended Azure Resources**
145-
146-
Recommended App service hosting the project //agent should fulfill this for each app instance
147-
- Application {{projectName}}
148-
- Hosting Service Type: {{azureComputeHost}} // it can be Azure Container Apps, Web App Service, Azure Functions, Azure Kubernetes Service. Recommend one based on the project.
149-
- SKU // recommend a sku based on the project, show its performance. Don't estimate the cost.
150-
- Configuration:
151-
- language: {language} //detect from the project, it can be nodejs, python, dotnet, etc.
152-
- dockerFilePath: {dockerFilePath}// fulfill this if service.azureComputeHost is ContainerApp
153-
- dockerContext: {dockerContext}// fulfill this if service.azureComputeHost is ContainerApp
154-
- Environment Variables: [] // the env variables that are used in the project/required by service
155-
- Dependencies Resource
156-
- Dependency Name
157-
- SKU // recommend a sku, show its performance.
158-
- Service Type // it can be Azure SQL, Azure Cosmos DB, Azure Storage, etc.
159-
- Connection Type // it can be connection string, managed identity, etc.
160-
- Environment Variables: [] // the env variables that are used in the project/required by dependency
161-
162-
Recommended Supporting Services
163-
- Application Insights
164-
- Log Analytics Workspace: set all app service to connect to this
165-
- Key Vault(Optional): If there are dependencies such as postgresql/sql/mysql, create a Key Vault to store connection string. If not, the resource should not show.
166-
If there is a Container App, the following resources are required:
167-
- Container Registry
168-
If there is a WebApp(App Service):
169-
- App Service Site Extension (Microsoft.Web/sites/siteextensions): Required for App Service deployments.
170-
171-
Recommended Security Configurations
172-
If there is a Container App
173-
- User managed identity: Must be assigned to the container app.
174-
- AcrPull role assignment: User managed identity must have **AcrPull** role ("7f951dda-4ed3-4680-a7ca-43fe172d538d") assigned to the container registry.
175-
176-
## **Execution Step**
177-
> **Below are the steps for Copilot to follow; ask Copilot to update or execute this plan.**
178-
{{string.Join(Environment.NewLine, steps)}}
179-
180-
""";
171+
return steps;
181172
}
182173
}

0 commit comments

Comments
 (0)