diff --git a/en/docs-nav.json b/en/docs-nav.json index a10ace33..f6b173b9 100644 --- a/en/docs-nav.json +++ b/en/docs-nav.json @@ -265,6 +265,10 @@ "text": "Add Module to Existing Microservices", "path": "guides/add-module-to-microservice.md" }, + { + "text": "Multi-tenant Domain Resolving in Microservice Solution", + "path": "guides/ms-multi-tenant-domain-resolver.md" + }, { "text": "Migrating to OpenIdDict", "path": "migration-guides/openIddict-microservice.md" diff --git a/en/guides/ms-multi-tenant-domain-resolver.md b/en/guides/ms-multi-tenant-domain-resolver.md new file mode 100644 index 00000000..95a8aefc --- /dev/null +++ b/en/guides/ms-multi-tenant-domain-resolver.md @@ -0,0 +1,516 @@ +# Multi-tenant Domain Resolving in Microservice Solution + +> This documentation introduces guidance for adding multi-tenant domain resolver to your microservice solution. This guide will use **mystore** as the main project name and you can simply replace it with your own project name to adept your solution. This guide will be using existing helm charts for deploying this solution on local kubernetes cluster with mystore domain name. + +## Configuring TenantResolver + +`DomainTenantResolver` is added via `AbpTenantResolveOptions`. Since all the applications, gateways and the microservices should resolve the subdomain as tenant, it can be configured it in `Shared.Hosting.AspNetCore` module class to make it available for all of them instead of repeating the same configuration. + +Navigate to the `MyStoreSharedHostingAspNetCoreModule` and add the following configuration to **ConfigureServices** method: + +```powershell +var configuration = context.Services.GetConfiguration(); + +Configure(options => +{ + options.AddDomainTenantResolver(configuration["TenantDomain"]); +}); +``` + +This configuration will allow to add a overridable **TenantDomain** key to the all applications, gateways and microservices' appsettings which will resolve the tenant based on the configuration. + +> If the `AbpTenantResolveOptions` namespace can not be found, add the `Volo.Abp.AspNetCore.MultiTenancy` package to your project. You can simply use `abp add-package Volo.Abp.AspNetCore.MultiTenancy` under the **MyStore.SharedHosting.AspNetCore** project. + +Now add this configuration to all the application, gateway and microservice appsettings: +```json +"TenantDomain": "https://{0}.authserver.mystore.dev" // in AuthServer appsettings.json +``` + +```json +"TenantDomain": "https://{0}.mystore.dev" // in PublicWeb appsettings.json +``` + +```json +"TenantDomain": "https://{0}.gateway-public-web.mystore.dev" // in PublicWebGateway appsettings.json +``` + +```json +"TenantDomain": "https://{0}.gateway-web.mystore.dev" // in WebGateway appsettings.json +``` + +```json +"TenantDomain": "https://{0}.administration.mystore.dev" // in AdministrationService appsettings.json +``` + +```json +"TenantDomain": "https://{0}.identity.mystore.dev" // in IdentityService appsettings.json +``` + +```json +"TenantDomain": "https://{0}.saas.mystore.dev" // in SaasService appsettings.json +``` + +```json +"TenantDomain": "https://{0}.product.mystore.dev" // in ProductService appsettings.json +``` + +This configuration will allow subdomain tenant resolving. Ex, if you have a tenant named **Volosoft**, the applications will resolve this tenant when you navigate to `https://volosoft.authserver.mystore.dev` (or any other app/gateway/microservice). + +> If you have more microservices, you need to add this configurations to your other microservice's appsettings.json aswell. + +**For angular application**, you don't need to add anything since we will be overriding the angular environment via kubernetes values file. + +## Configuring AuthServer + +When the tenant try to login from an application (Ex `https://volosoft.angular.mystore.dev`) it will be redirected to AuthServer (`https://volosoft.authserver.mystore.dev`) and you will be seeing a **HTTP 400 error** related to `invalid redirect_uri`. If you check the authserver application logs (under Logs/logs.txt file or console logs), you will notice that the `https://volosoft.angular.mystore.dev` is not a valid redirect_uri since it is not been seeded by the OpenIddictDataSeeder. Only the **host** applications, gateways and microservice URLs are seeded (like `https://angular.mystore.dev`). +Instead of trying to manually add all the tenant domains, you can use **enable AbpOpenIddict wildcard domain support**. That will enable the subdomain support, saving you from manually entering all the *redirect* and *post_logout redirect* URIs. + +To enable it, navigate to **MyStoreAuthServerModule** and update the `PreConfigureServices` method: + +```csharp +var hostingEnvironment = context.Services.GetHostingEnvironment(); +var configuration = context.Services.GetConfiguration(); + +PreConfigure(options => +{ + options.EnableWildcardDomainSupport = true; + options.WildcardDomainsFormat.Add(configuration["WildCardDomains:AuthServer"]); + options.WildcardDomainsFormat.Add(configuration["WildCardDomains:Angular"]); + options.WildcardDomainsFormat.Add(configuration["WildCardDomains:PublicWeb"]); + options.WildcardDomainsFormat.Add(configuration["WildCardDomains:WebGateway"]); + options.WildcardDomainsFormat.Add(configuration["WildCardDomains:PublicWebGateway"]); + options.WildcardDomainsFormat.Add(configuration["WildCardDomains:IdentityService"]); + options.WildcardDomainsFormat.Add(configuration["WildCardDomains:AdministrationService"]); + options.WildcardDomainsFormat.Add(configuration["WildCardDomains:SaasService"]); + options.WildcardDomainsFormat.Add(configuration["WildCardDomains:ProductService"]); +}); +``` + +This configuration will automatically handle the *redirect* and *post_logout redirect* URIs. + +Add the configurations to the `appsettings.json` file of the AuthServer project: + +```json +"WildCardDomains": { + "AuthServer": "https://{0}.authserver.mystore.dev", + "Angular": "https://{0}.angular.mystore.dev", + "PublicWeb": "https://{0}.mystore.dev", + "WebGateway": "https://{0}.gateway-web.mystore.dev", + "PublicWebGateway": "https://{0}.gateway-public-web.mystore.dev", + "IdentityService": "https://{0}.identity.mystore.dev", + "AdministrationService": "https://{0}.administration.mystore.dev", + "SaasService": "https://{0}.saas.mystore.dev", + "ProductService": "https://{0}.product.mystore.dev" + }, +``` + +If you have more microservices or applications, you need to add them to handle the *redirect* and *post_logout redirect* URIs of them aswell. + +## Configuring PublicWeb/Web/Blazor Server + +The server-side rendering applications like public-web, web and blazor-server apps are using hybrid flow authorization flow. Which contains additional configuration on the module when hosted on containerized environment: + +```csharp +if (Convert.ToBoolean(configuration["AuthServer:IsOnK8s"])) +{ + context.Services.Configure("oidc", options => + { + options.MetadataAddress = configuration["AuthServer:MetaAddress"]!.EnsureEndsWith('/') + + ".well-known/openid-configuration"; + + var previousOnRedirectToIdentityProvider = options.Events.OnRedirectToIdentityProvider; + options.Events.OnRedirectToIdentityProvider = async ctx => + { + // Intercept the redirection so the browser navigates to the right URL in your host + ctx.ProtocolMessage.IssuerAddress = configuration["AuthServer:Authority"]!.EnsureEndsWith('/') + "connect/authorize"; + + if (previousOnRedirectToIdentityProvider != null) + { + await previousOnRedirectToIdentityProvider(ctx); + } + }; + + var previousOnRedirectToIdentityProviderForSignOut = options.Events.OnRedirectToIdentityProviderForSignOut; + options.Events.OnRedirectToIdentityProviderForSignOut = async ctx => + { + // Intercept the redirection for signout so the browser navigates to the right URL in your host + ctx.ProtocolMessage.IssuerAddress = configuration["AuthServer:Authority"]!.EnsureEndsWith('/') + "connect/logout"; + + if (previousOnRedirectToIdentityProviderForSignOut != null) + { + await previousOnRedirectToIdentityProviderForSignOut(ctx); + } + }; + }); +} +``` + +This configuration contains the *real DNS* of the AuthServer (configuration["AuthServer:Authority"]) and the *.well-known endpoint* (configuration["AuthServer:MetaAddress"]) used to obtain the tokens fromthe internal network. The configuration intercepts the login and logout requests from the browser to redirect to real DNS. When the tenant try to login from the one of these application (Ex `https://volosoft.mystore.dev`) it should be redirected to **tenant's AuthServer** (`https://volosoft.authserver.mystore.dev`) instead of the host's AuthServer (`https://authserver.mystore.dev`). + +You need to update this configuration as below to support this functionality: + +```csharp +if (Convert.ToBoolean(configuration["AuthServer:IsOnK8s"])) +{ + context.Services.Configure("oidc", options => + { + options.MetadataAddress = configuration["AuthServer:MetaAddress"]!.EnsureEndsWith('/') + + ".well-known/openid-configuration"; + + var previousOnRedirectToIdentityProvider = options.Events.OnRedirectToIdentityProvider; + options.Events.OnRedirectToIdentityProvider = async ctx => + { + // Intercept the redirection so the browser navigates to the right URL in your host + ctx.ProtocolMessage.IssuerAddress = configuration["AuthServer:Authority"]!.EnsureEndsWith('/') + "connect/authorize"; + + // Resolve the current tenant + var currentTenant = ctx.HttpContext.RequestServices.GetRequiredService(); + var tenantDomain = configuration["TenantDomain"]; + + // Check if the current tenant is available and the solution is using domain tenant resolver + if (currentTenant.IsAvailable && !string.IsNullOrEmpty(tenantDomain)) + { + // Replace "{0}"" string for the tenant authserver. The authority value should be replaced from + // https://{0}.authserver.mystore.dev to https://tenantName.authserver.mystore.dev + ctx.ProtocolMessage.IssuerAddress = ctx.ProtocolMessage.IssuerAddress.Replace("{0}", $"{currentTenant.Name}"); + } + else + { + // Keep using the host authserver if there is no tenant + ctx.ProtocolMessage.IssuerAddress = ctx.ProtocolMessage.IssuerAddress.Replace("{0}.", string.Empty); + } + + if (previousOnRedirectToIdentityProvider != null) + { + await previousOnRedirectToIdentityProvider(ctx); + } + }; + + var previousOnRedirectToIdentityProviderForSignOut = options.Events.OnRedirectToIdentityProviderForSignOut; + // Similar configuration for Logout request + options.Events.OnRedirectToIdentityProviderForSignOut = async ctx => + { + // Intercept the redirection for signout so the browser navigates to the right URL in your host + ctx.ProtocolMessage.IssuerAddress = configuration["AuthServer:Authority"]!.EnsureEndsWith('/') + "connect/logout"; + + var currentTenant = ctx.HttpContext.RequestServices.GetRequiredService(); + var tenantDomain = configuration["TenantDomain"]; + if (currentTenant.IsAvailable && !string.IsNullOrEmpty(tenantDomain)) + { + ctx.ProtocolMessage.IssuerAddress = ctx.ProtocolMessage.IssuerAddress.Replace("{0}", $"{currentTenant.Name}"); + } + else + { + ctx.ProtocolMessage.IssuerAddress = ctx.ProtocolMessage.IssuerAddress.Replace("{0}.", string.Empty); + } + + if (previousOnRedirectToIdentityProviderForSignOut != null) + { + await previousOnRedirectToIdentityProviderForSignOut(ctx); + } + }; + }); +} +``` + +Now you can override the `[AuthServer:Authority]` configuration of your public-web, mvc or blazor-server applications by using `https://{0}.authserver.mystore.dev`. + +## Configuring the Microservices for SwaggerUI + +The SwaggerUI for the gateways and the microservices are also being configured to resolve the domain as below. Hence, when navigated to the tenant's gateway (or any microservice) SwaggerUI, the Authorization disovery endpoint should be pointing to the tenant's AuthServer instead of the Host's. + +Configure the `MetadataAddress` of the SwaggerUI AuthServer configuration to use `https://{0}.authserver.mystore.dev` so the discovery endpoint can be resolved correctly. + +> This feature is available after ABP v7.5. + +## Configuring Helm + +This configuration will be related to **local Kubernetes cluster deployment** to help you understand how to override the related domain values for the production environment. Some of the steps are optional since there are other ways to handle the *problem*. + +### Configuring the Host File + +**Problem:** I created a tenant with the name **Volosoft** but when I navigate to tenant application (`https://volosoft.angular.mystore.dev`) the DNS is not resolved and I get the error **Can't reach this page** `DNS_PROBE_FINISHED_NXDOMAIN`. + +**Solution:** You need to map the connection between the local kubernetes IP address and the domain name server. Simply update your `Windows/system32/drivers/etc/hosts` (or `/etc/hosts` in linux and macos) file assuming you have will use a tenant with name **Volosoft**: + +```txt +127.0.0.1 volosoft.angular.mystore.dev +127.0.0.1 volosoft.mystore.dev +127.0.0.1 volosoft.authserver.mystore.dev +127.0.0.1 volosoft.identity.mystore.dev +127.0.0.1 volosoft.administration.mystore.dev +127.0.0.1 volosoft.product.mystore.dev +127.0.0.1 volosoft.saas.mystore.dev +127.0.0.1 volosoft.gateway-web.mystore.dev +127.0.0.1 volosoft.gateway-public.mystore.dev +``` + +### Configuring SSL Certificate + +**Problem:** The default self-signed certificate generation powershell script in the template is only for the host application: + +```powershell +mkcert "mystore.dev" "*.mystore.dev" +``` + +When you navigate to a subdomain like `https://volosoft.authserver.mystore.dev`, the certificate doesn't cover this domain since it doesn't support 2nd depth of sub-domains. Hence, when you navigate `https://volosoft.authserver.mystore.dev` in your local staging environment, there is an untrusted SSL error. + +You can create a different self-signed SSL certificate or expand the extending certificate to be used for sub-domains aswell. To expand it, simply update the `create-tls-secrets.ps1` script (under the k8s/Mystore folder) with: + +```powershell +mkcert "mystore.dev" "*.mystore.dev" "*.angular.mystore.dev" "*.mystore.dev" "*.authserver.mystore.dev" "*.identity.mystore.dev" "*.administration.mystore.dev" "*.saas.mystore.dev" "*.product.mystore.dev" "*.gateway-web.mystore.dev" "*.gateway-public.mystore.dev" +``` + +After you run the `create-tls-secrets.ps1` script, now it should create kubernetes tls secret for the subdomains aswell. + +### Configuring Ingress + +Your request from the browser to the subdomain will be accepted by the ingress-controller. The ingress-controller will redirect to the chart ingress based on your request. + +Update your charts ingress.yaml files. Ex for **administration-ingress.yaml**: + +```yaml +spec: + tls: + - hosts: + - {{ .Values.ingress.host }} + secretName: {{ .Values.ingress.tlsSecret }} + # An other host with wildcard which will be using the same tls secret created above + - hosts: + - "*.{{ .Values.ingress.host }}" + secretName: {{ .Values.ingress.tlsSecret }} + rules: + - host: "{{ .Values.ingress.host }}" + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: {{ .Release.Name }}-{{ .Chart.Name }} + port: + number: 80 + # An other host with wildcard which will be responding to `volosoft.administration.mystore.dev` + - host: "*.{{ .Values.ingress.host }}" + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: {{ .Release.Name }}-{{ .Chart.Name }} + port: + number: 80 +``` + +**Update all the application, gateway and microservice ingress.yaml files.** Eventually, when deploy the application, you will be seeing: +![updated-ingress](../images/updated-ingress.png) + +### Overriding Environment Variables + +Navigate to applications, gateways and microservices' **x-deployment.yaml** file and override the newly introduced `TenantDomain` key: + +```yaml +... Removed for brevity +- name: "TenantDomain" # Add this key + value: "{{ .Values.config.tenantDomain }}" # Override value under the .Values.yaml file +- name: "AuthServer__Authority" + value: "{{ .Values.config.authServer.authority }}" +... +``` + +> **Update all the application, gateway and microservice deployment.yaml files.** + +**For AuthServer, also add the WildCardDomains that is used to handle subdomain *redirect* and *post_logout redirect* URIs to the authserver-deployment.yaml file:** + +```yaml +... Removed for brevity +- name: "TenantDomain" + value: "{{ .Values.config.tenantDomain }}" +- name: "WildCardDomains__AuthServer" + value: "{{ .Values.wildCardDomains.authServer }}" +- name: "WildCardDomains__Angular" + value: "{{ .Values.wildCardDomains.angular }}" +- name: "WildCardDomains__PublicWeb" + value: "{{ .Values.wildCardDomains.publicWeb }}" +- name: "WildCardDomains__WebGateway" + value: "{{ .Values.wildCardDomains.webGateway }}" +- name: "WildCardDomains__PublicWebGateway" + value: "{{ .Values.wildCardDomains.publicWebGateway }}" +- name: "WildCardDomains__IdentityService" + value: "{{ .Values.wildCardDomains.identityService }}" +- name: "WildCardDomains__AdministrationService" + value: "{{ .Values.wildCardDomains.administrationService }}" +- name: "WildCardDomains__SaasService" + value: "{{ .Values.wildCardDomains.saasService }}" +- name: "WildCardDomains__ProductService" + value: "{{ .Values.wildCardDomains.productService }}" +... +``` + +Afterwards, update the **values.yaml** file for all the sub-charts (administration, authserver etc): + +```yaml +config: + ... Removed for brevity + tenantDomain: # +``` + +You can either enter the domain values here (Ex, for administrationService: `"https://{0}.administration.mystore.dev"` ) or override these values aswell on the **main chart values.yaml** file. + +**For AuthServer, also update the authserver .values.yaml file:** + +```yaml +config: + ... Removed for brevity + tenantDomain: # + +wildCardDomains: + authServer: # + angular: # + publicWeb: # + webGateway: + publicWebGateway: # + identityService: # + administrationService: # + saasService: # + productService: # +``` + +### Overriding Tenant Domain Values + +After updating the **deployment.yaml** and **values.yaml** files of all the application, gateway and microservices' navigate to the **mystore chart values.yaml** file located under the k8s/Mystore/values.yaml that overrides all the sub-charts: + +**AuthServer:** + +```yaml +# auth-server sub-chart override +authserver: + config: + ... Removed for brevity + corsOrigins: https://*.angular.mystore.dev,https://angular.mystore.dev,https://gateway-web.mystore.dev,https://*.gateway-web.mystore.dev,https://gateway-public.mystore.dev,https://*.gateway-public.mystore.dev,https://identity.mystore.dev,https://*.identity.mystore.dev,https://administration.mystore.dev,https://*.administration.mystore.dev,https://saas.mystore.dev,https://*.saas.mystore.dev,https://product.mystore.dev,https://*.product.mystore.dev + tenantDomain: "https://{0}.authserver.mystore.dev" + wildCardDomains: + authServer: "https://{0}.authserver.mystore.dev" + angular: "https://{0}.angular.mystore.dev" + publicWeb: "https://{0}.mystore.dev" + webGateway: "https://{0}.gateway-web.mystore.dev" + publicWebGateway: "https://{0}.gateway-public-web.mystore.dev" + identityService: "https://{0}.identity.mystore.dev" + administrationService: "https://{0}.administration.mystore.dev" + saasService: "https://{0}.saas.mystore.dev" + productService: "https://{0}.product.mystore.dev" +``` + +You may also get CORS error when authenticating SwaggerUI of your gateways or microservices. Add Override the AuthServer CORS values with the subdomain to solve this problem: + +**identityService:** + +```yaml +# identity-service sub-chart override +identity: + config: + authServer: + metadataAddress: https://{0}.authserver.mystore.dev + ... Removed for brevity + tenantDomain: "https://{0}.identity.mystore.dev" +``` + +**administrationService:** + +```yaml +# administration-service sub-chart override +administration: + config: + authServer: + metadataAddress: https://{0}.authserver.mystore.dev + ... Removed for brevity + tenantDomain: "https://{0}.administration.mystore.dev" +``` + +**saasService:** + +```yaml +# saas-service sub-chart override +saas: + config: + authServer: + metadataAddress: https://{0}.authserver.mystore.dev + ... Removed for brevity + tenantDomain: "https://{0}.saas.mystore.dev" +``` + +**productService:** + +```yaml +# product-service sub-chart override +product: + config: + authServer: + metadataAddress: https://{0}.authserver.mystore.dev + ... Removed for brevity + tenantDomain: "https://{0}.product.mystore.dev" +``` + +**gateway-web:** + +```yaml +# saas-service sub-chart override +gateway-web: + config: + authServer: + metadataAddress: https://{0}.authserver.mystore.dev + ... Removed for brevity + tenantDomain: "https://{0}.gateway-web.mystore.dev" +``` + +**gateway-web-public:** + +```yaml +# gateway-web-public sub-chart override +gateway-web-public: + config: + authServer: + metadataAddress: https://{0}.authserver.mystore.dev + ... Removed for brevity + tenantDomain: "https://{0}.gateway-public.mystore.dev" +``` + +**publicweb:** + +```yaml +# Public Web application sub-chart override +publicweb: + config: + ... Removed for brevity + authServer: + authority: https://{0}.authserver.mystore.dev # should be the domain name (https://auth.mydomain.com) + ... Removed for brevity + tenantDomain: "https://{0}.mystore.dev" +``` + +**angular:** + +```yaml +# Angular back-office application sub-chart override +angular: + config: + selfUrl: https://{0}.angular.mystore.dev + gatewayUrl: https://{0}.gateway-web.mystore.dev + authServer: + authority: https://{0}.authserver.mystore.dev + requireHttpsMetadata: "false" + responseType: "code" + strictDiscoveryDocumentValidation: false + skipIssuerCheck: true +``` + +> If you are using Web or BlazorServer application for back-office, it is similar configuration with the public-web application + +Now you can use the `deploy-staging.ps1` script to deploy your solution. + +## Result + +After creating a new tenant named Volosoft from the angular back-office application: + +![angular-back-office](../images/ms-domain-resolver.gif) diff --git a/en/images/ms-angular-back-office.png b/en/images/ms-angular-back-office.png new file mode 100644 index 00000000..40ee646c Binary files /dev/null and b/en/images/ms-angular-back-office.png differ diff --git a/en/images/ms-domain-resolver.gif b/en/images/ms-domain-resolver.gif new file mode 100644 index 00000000..1553639e Binary files /dev/null and b/en/images/ms-domain-resolver.gif differ diff --git a/en/images/updated-ingress.png b/en/images/updated-ingress.png new file mode 100644 index 00000000..661a7d58 Binary files /dev/null and b/en/images/updated-ingress.png differ diff --git a/en/studio/images/abp-studio-beta-access.png b/en/studio/images/abp-studio-beta-access.png new file mode 100644 index 00000000..d509224c Binary files /dev/null and b/en/studio/images/abp-studio-beta-access.png differ diff --git a/en/studio/images/new-version-available-window.png b/en/studio/images/new-version-available-window.png new file mode 100644 index 00000000..d38c231d Binary files /dev/null and b/en/studio/images/new-version-available-window.png differ diff --git a/en/studio/images/preference-theme-change.png b/en/studio/images/preference-theme-change.png new file mode 100644 index 00000000..8e2dc109 Binary files /dev/null and b/en/studio/images/preference-theme-change.png differ diff --git a/en/studio/quick-starts/images/abp-studio-background-tasks.png b/en/studio/quick-starts/images/abp-studio-background-tasks.png new file mode 100644 index 00000000..cf633946 Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-background-tasks.png differ diff --git a/en/studio/quick-starts/images/abp-studio-created-microservice-solution-explorer.png b/en/studio/quick-starts/images/abp-studio-created-microservice-solution-explorer.png new file mode 100644 index 00000000..97936d01 Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-created-microservice-solution-explorer.png differ diff --git a/en/studio/quick-starts/images/abp-studio-created-new-microservice-solution.png b/en/studio/quick-starts/images/abp-studio-created-new-microservice-solution.png new file mode 100644 index 00000000..7b137365 Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-created-new-microservice-solution.png differ diff --git a/en/studio/quick-starts/images/abp-studio-microservice-example-identity-service-files.png b/en/studio/quick-starts/images/abp-studio-microservice-example-identity-service-files.png new file mode 100644 index 00000000..852e76dd Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-microservice-example-identity-service-files.png differ diff --git a/en/studio/quick-starts/images/abp-studio-microservice-example-identity-service-in-visual-studio.png b/en/studio/quick-starts/images/abp-studio-microservice-example-identity-service-in-visual-studio.png new file mode 100644 index 00000000..1c11d579 Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-microservice-example-identity-service-in-visual-studio.png differ diff --git a/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-application-monitor.png b/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-application-monitor.png new file mode 100644 index 00000000..d58bf7fa Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-application-monitor.png differ diff --git a/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-build-docker-images.png b/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-build-docker-images.png new file mode 100644 index 00000000..b2c3115f Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-build-docker-images.png differ diff --git a/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-disable-interception.png b/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-disable-interception.png new file mode 100644 index 00000000..e9cebaa6 Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-disable-interception.png differ diff --git a/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-enable-interception.png b/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-enable-interception.png new file mode 100644 index 00000000..c9ed3411 Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-enable-interception.png differ diff --git a/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-install-helm-chart.png b/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-install-helm-chart.png new file mode 100644 index 00000000..c4e4f678 Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-install-helm-chart.png differ diff --git a/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-interception-enabled.png b/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-interception-enabled.png new file mode 100644 index 00000000..bf4e2906 Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-interception-enabled.png differ diff --git a/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-interception-iis-express-warning.png b/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-interception-iis-express-warning.png new file mode 100644 index 00000000..68072a4e Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-interception-iis-express-warning.png differ diff --git a/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-redeploy.png b/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-redeploy.png new file mode 100644 index 00000000..2636645e Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-redeploy.png differ diff --git a/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-services-browse.png b/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-services-browse.png new file mode 100644 index 00000000..88263403 Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-services-browse.png differ diff --git a/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-services.png b/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-services.png new file mode 100644 index 00000000..fb6c0d35 Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-services.png differ diff --git a/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-tab.png b/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-tab.png new file mode 100644 index 00000000..16ff7182 Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-microservice-kubernetes-tab.png differ diff --git a/en/studio/quick-starts/images/abp-studio-microservice-role-management-permissions.png b/en/studio/quick-starts/images/abp-studio-microservice-role-management-permissions.png new file mode 100644 index 00000000..09ff964c Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-microservice-role-management-permissions.png differ diff --git a/en/studio/quick-starts/images/abp-studio-microservice-solution-runner-applications.png b/en/studio/quick-starts/images/abp-studio-microservice-solution-runner-applications.png new file mode 100644 index 00000000..0a4f7da5 Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-microservice-solution-runner-applications.png differ diff --git a/en/studio/quick-starts/images/abp-studio-microservice-solution-runner-browse-microservice.png b/en/studio/quick-starts/images/abp-studio-microservice-solution-runner-browse-microservice.png new file mode 100644 index 00000000..6b15ce18 Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-microservice-solution-runner-browse-microservice.png differ diff --git a/en/studio/quick-starts/images/abp-studio-microservice-solution-runner-browse.png b/en/studio/quick-starts/images/abp-studio-microservice-solution-runner-browse.png new file mode 100644 index 00000000..da827115 Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-microservice-solution-runner-browse.png differ diff --git a/en/studio/quick-starts/images/abp-studio-microservice-solution-runner-build-all.png b/en/studio/quick-starts/images/abp-studio-microservice-solution-runner-build-all.png new file mode 100644 index 00000000..575ed6b8 Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-microservice-solution-runner-build-all.png differ diff --git a/en/studio/quick-starts/images/abp-studio-microservice-solution-runner-docker-dependencies.png b/en/studio/quick-starts/images/abp-studio-microservice-solution-runner-docker-dependencies.png new file mode 100644 index 00000000..99c09045 Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-microservice-solution-runner-docker-dependencies.png differ diff --git a/en/studio/quick-starts/images/abp-studio-microservice-solution-runner-enable-watch.png b/en/studio/quick-starts/images/abp-studio-microservice-solution-runner-enable-watch.png new file mode 100644 index 00000000..2efe8e53 Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-microservice-solution-runner-enable-watch.png differ diff --git a/en/studio/quick-starts/images/abp-studio-microservice-solution-runner-external-service.png b/en/studio/quick-starts/images/abp-studio-microservice-solution-runner-external-service.png new file mode 100644 index 00000000..35fc5189 Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-microservice-solution-runner-external-service.png differ diff --git a/en/studio/quick-starts/images/abp-studio-microservice-solution-runner-watch-enabled-icon.png b/en/studio/quick-starts/images/abp-studio-microservice-solution-runner-watch-enabled-icon.png new file mode 100644 index 00000000..796d183d Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-microservice-solution-runner-watch-enabled-icon.png differ diff --git a/en/studio/quick-starts/images/abp-studio-microservice-solution-runner.png b/en/studio/quick-starts/images/abp-studio-microservice-solution-runner.png new file mode 100644 index 00000000..2b5bc206 Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-microservice-solution-runner.png differ diff --git a/en/studio/quick-starts/images/abp-studio-microservice-sql-server-connection.png b/en/studio/quick-starts/images/abp-studio-microservice-sql-server-connection.png new file mode 100644 index 00000000..cb87549f Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-microservice-sql-server-connection.png differ diff --git a/en/studio/quick-starts/images/abp-studio-microservice-sql-server-databases.png b/en/studio/quick-starts/images/abp-studio-microservice-sql-server-databases.png new file mode 100644 index 00000000..65b42c60 Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-microservice-sql-server-databases.png differ diff --git a/en/studio/quick-starts/images/abp-studio-microservice-web-application-home-page.png b/en/studio/quick-starts/images/abp-studio-microservice-web-application-home-page.png new file mode 100644 index 00000000..0b220166 Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-microservice-web-application-home-page.png differ diff --git a/en/studio/quick-starts/images/abp-studio-microservice-web-role-permissions.png b/en/studio/quick-starts/images/abp-studio-microservice-web-role-permissions.png new file mode 100644 index 00000000..f4e45627 Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-microservice-web-role-permissions.png differ diff --git a/en/studio/quick-starts/images/abp-studio-new-microservice-helm-charts.png b/en/studio/quick-starts/images/abp-studio-new-microservice-helm-charts.png new file mode 100644 index 00000000..42009900 Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-new-microservice-helm-charts.png differ diff --git a/en/studio/quick-starts/images/abp-studio-new-microservice-solution-dialog-optional-modules.png b/en/studio/quick-starts/images/abp-studio-new-microservice-solution-dialog-optional-modules.png new file mode 100644 index 00000000..3bc6a80f Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-new-microservice-solution-dialog-optional-modules.png differ diff --git a/en/studio/quick-starts/images/abp-studio-new-microservice-solution-dialog-properties.png b/en/studio/quick-starts/images/abp-studio-new-microservice-solution-dialog-properties.png new file mode 100644 index 00000000..f27d28a0 Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-new-microservice-solution-dialog-properties.png differ diff --git a/en/studio/quick-starts/images/abp-studio-new-solution-dialog-database-configurations-microservice.png b/en/studio/quick-starts/images/abp-studio-new-solution-dialog-database-configurations-microservice.png new file mode 100644 index 00000000..4472d294 Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-new-solution-dialog-database-configurations-microservice.png differ diff --git a/en/studio/quick-starts/images/abp-studio-new-solution-dialog-database-provider-microservice.png b/en/studio/quick-starts/images/abp-studio-new-solution-dialog-database-provider-microservice.png new file mode 100644 index 00000000..60606126 Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-new-solution-dialog-database-provider-microservice.png differ diff --git a/en/studio/quick-starts/images/abp-studio-new-solution-dialog-microservice.png b/en/studio/quick-starts/images/abp-studio-new-solution-dialog-microservice.png new file mode 100644 index 00000000..e98c96ac Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-new-solution-dialog-microservice.png differ diff --git a/en/studio/quick-starts/images/abp-studio-new-solution-dialog-mobile-framework-microservice.png b/en/studio/quick-starts/images/abp-studio-new-solution-dialog-mobile-framework-microservice.png new file mode 100644 index 00000000..661efff4 Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-new-solution-dialog-mobile-framework-microservice.png differ diff --git a/en/studio/quick-starts/images/abp-studio-new-solution-dialog-ui-framework-microservice.png b/en/studio/quick-starts/images/abp-studio-new-solution-dialog-ui-framework-microservice.png new file mode 100644 index 00000000..ffd6e736 Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-new-solution-dialog-ui-framework-microservice.png differ diff --git a/en/studio/quick-starts/images/abp-studio-new-solution-dialog-ui-theme-microservice.png b/en/studio/quick-starts/images/abp-studio-new-solution-dialog-ui-theme-microservice.png new file mode 100644 index 00000000..350dab61 Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-new-solution-dialog-ui-theme-microservice.png differ diff --git a/en/studio/quick-starts/images/abp-studio-open-module-folder.png b/en/studio/quick-starts/images/abp-studio-open-module-folder.png new file mode 100644 index 00000000..b32f046d Binary files /dev/null and b/en/studio/quick-starts/images/abp-studio-open-module-folder.png differ