Visual Studio “macro” search and replace

Have you ever try to edit/replace a large object? Recently, I need to add a method to a Typescript object with over 50 properties. This static method deals with ALL properties of the object. The class properties look like this:

activationDate: Date;
activationDateSpecified: boolean;
authorizations: any;
authorizedLocationOfCares: any;
currentLocation: number;
currentLocationDetail: any;
currentLocationSpecified: boolean;
data2000: any;
deaNumber: any;
errorDetails: any;
expirationDate: Date;
expirationDateSpecified: boolean;
firstName: string;
globalID: number;
globalIDSpecified: boolean;
groupList: any;
homeLocation: number;
homeLocationDetail: any;
homeLocationSpecified: boolean;
...

I create a method and copy a list of properties. However, I need to change every property to:
userDetailExternal.[propertyname] = _obj.[propertyname];
For example:

userDetailExternal.activationDateSpecified = _obj.activationDateSpecified;

I used Replace (Ctrl+H) with the following:
Use Regular Expressions (Alt+E)
In the Find field:

([a-zA-Z0-9]+):\s*([^\n\r]*)

In the Replace field:

userDetailExternal.$1 = _obj.$1;

Be careful not to replace the declaration of the properties.

The Azure pitfall with Let’s Encrypt

This month my Azure bill jumped high for using the Microsoft Azure App Services. I expected to pay around $54.75 for a B1 tear. I think it’s very high for 2 basic websites with minimum traffic. 4

I noticed a significant increase this month to the $82. I went into the bill details and found charges for using SSL!

Wait a minute. Did I already pay for the Custom domain/SSL? Apparently, (see the small print) Microsoft SSL is only SNI SSL. Azure charged extra for IP SSL.
$39 for using my own SSL certificate?!!! This is per certificate!!!
I have just two websites on an App Service and I’m charged more for the certificates.

3

If you get charged for it, you should request a refund.

Here is how you can fix it if you are using the Let’s Encrypt Extension:

1

You should Unclick this check-box.  You will receive the SNI SSL certificate.

That’s it. Good luck with the Azure refund. ūüėÉ

Migrate Angular SSR from .net core 2 to core 3.1 Angular 9 with SSR. Part 1

Asp.Net core 3.1 dropped the UseSpaPrerendering. So, no more server-side rendering (SSR). If you have Asp.Net core 2+ single page application (SPA) with SSR – no road map from Microsoft. Well, not exactly. Here and there you may find some ideas, but not a complete path with step-by-step migration instruction.

I want to give you a hint: iisnode. The iisnode exist for a long time. Azure supports it (https://github.com/Azure/iisnode). The iisnode with rewrite may do the job. However, I faced a small problem: iisnode does not play well with the IIS Express 10. Obviously, use IIS for development, the first that comes to my mind. But now I have to ask the entire team to install and manage IIS.

The default, out of the box, solution from Microsoft: UseProxyToSpaDevelopmentServer. This method just proxy all requests to an instance of the Node Express running in a separate process. Ok, the iisnode should not be used for development, but in a production environment only.

The web.config transformation can help here. Add a file called web.Release.config:

<?xml version="1.0"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
  <system.webServer>

    <!-- Web.Debug.config adds attributes to this to enable remote debugging when publishing in Debug configuration. -->
    <iisnode watchedFiles="web.config;*.js" flushResponse="true" xdt:Transform="InsertIfMissing" />
    <!-- Remote debugging (Azure Website with git deploy): Comment out iisnode above, and uncomment iisnode below. -->
    <!--<iisnode watchedFiles="web.config;*.js"
      loggingEnabled="true"
      devErrorsEnabled="true"
      nodeProcessCommandLine="node.exe --debug"/>-->

    <handlers>
      <add name="aspNetCore" path="api" xdt:Locator="Match(name)" xdt:Transform="SetAttributes"/>

      <add name="iisnode" path="ClientApp/iisnode.js" verb="*" modules="iisnode" resourceType="Unspecified" responseBufferLimit="0" xdt:Locator="Match(name)" xdt:Transform="Insert"/>
      <!-- Remote debugging (Azure Website with git deploy): Uncomment NtvsDebugProxy handler below.
      Additionally copy Microsoft.NodejsTools.WebRole to 'bin' from the Remote Debug Proxy folder.-->
      <!-- The GUID in the following path is meant to protect the debugging endpoint against inadvertent access, and should be treated as a password. -->
      <!--<add name="NtvsDebugProxy" path="ntvs-debug-proxy/5c950099-1b79-430f-967d-fa523eb222b7" verb="*" resourceType="Unspecified"
        type="Microsoft.NodejsTools.Debugger.WebSocketProxy, Microsoft.NodejsTools.WebRole" xdt:Locator="Match(name)" xdt:Transform="Insert" />-->
    </handlers>
    <rewrite xdt:Transform="Insert">
      <rules>
        <clear />
        <!-- Remote debugging (Azure Website with git deploy): Uncomment the NtvsDebugProxy rule below. -->
        <!--<rule name="NtvsDebugProxy" enabled="true" stopProcessing="true">
          <match url="^ntvs-debug-proxy/.*"/>
        </rule>-->
        <rule name="Api" patternSyntax="ECMAScript" stopProcessing="true">
          <match url="api/(.*)" />
        </rule>
        <rule name="ClientApp" enabled="true" patternSyntax="ECMAScript" stopProcessing="true">
          <match url="iisnode.+" negate="true" />
          <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
          <action type="Rewrite" url="ClientApp/iisnode.js" />
        </rule>
      </rules>
    </rewrite>
  </system.webServer>
</configuration>

 There are several points:

  1. All api/* routes are processing by aspNetCore.
  2. Everything else are processing by the iisnode.
  3. The handlers section changes the aspNetCore. The original path=”*” replaced with path=”api”.¬†
  4. The handlers section added the iisnode entry.
  5. Finally, the entire rewrite block added to manage routes.

Pay attention to the path=”ClientApp/iisnode.js” attribute in the iisnode element. You will need to add this iisnode.js file to the ClientApp. This file contains only one line:

require(__dirname + '\\dist-server\\main.js');

As this file will not be build by Angular, add this file to the project output directory. Set the “Build Action” to “None” and “Copy to Output Directory” to “Copy if newer”

Next, add another file: server.ts. I took this file from the Angular Universal sample. Navigate to https://angular.io/guide/universal and search for “Download the finished sample code”. I made several adjustments. Here is the entire file:

import 'zone.js/dist/zone-node';

import { ngExpressEngine } from '@nguniversal/express-engine';
import * as express from 'express';
import { join } from 'path';

import { AppServerModule } from './main.server';
import { APP_BASE_HREF } from '@angular/common';
import { existsSync } from 'fs';
import { BASE_URL } from './app/tokens/base-url.token';
import * as compression from 'compression';
import * as helmet from 'helmet';

// The Express app is exported so that it can be used by serverless Functions.
export function app() {
    const server = express();
    const distFolder = join(process.cwd(), 'dist');
    const indexHtml = existsSync(join(distFolder, 'index.original.html')) ? 'index.original.html' : 'index.html';

    // Our Universal express-engine (found @ https://github.com/angular/universal/tree/master/modules/express-engine)
    server.engine('html', ngExpressEngine({
        bootstrap: AppServerModule,
    }));

    // Security
    server.use(helmet());

    server.use(compression({ filter: shouldCompress }));
    server.set('view engine', 'html');
    server.set('views', distFolder);


    // TODO: implement data requests securely
    server.get('/api/**', (_req, res) => {
        res.status(404).send('data requests are not yet supported');
    });

    server.get('*.json', express.json({
        limit: '1000kb'
    }));

    // Serve static files from /browser
    server.get('*.*', express.static(distFolder, {
        immutable: true,
        maxAge: '1y'
    }));

    // All regular routes use the Universal engine
    server.get('*', (req, res) => {
        const baseUrl = req.protocol + '://' + ((req.headers.host !== undefined) ? req.headers.host : req.hostname);
        res.render(indexHtml, {
            req, providers: [
                { provide: APP_BASE_HREF, useValue: req.baseUrl },
                { provide: BASE_URL, useValue: baseUrl }
            ]
        });
    });

    return server;
}

function shouldCompress(req, res) {
    if (req.headers['x-no-compression']) {
        // don't compress responses with this request header
        return false
    }

    // fallback to standard filter function
    return compression.filter(req, res)
}

function run() {
    const port = process.env.PORT || 4000;

    // Start up the Node server
    const server = app();
    server.listen(port, () => {
        console.log(`Node Express server listening on http://localhost:${port}`);
    });
}

// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
    run();
}

export * from './main.server';

You may have to adjust the code to implement your own providers.

As you are no longer using the aspnet-prerendering, your main.server.ts can be simplified:

import '@angular/localize/init';
import { enableProdMode } from '@angular/core';
import { environment } from './environments/environment';

if (environment.production) {
  enableProdMode();
}

export { AppServerModule } from './app/app.server.module';
export { renderModule, renderModuleFactory } from '@angular/platform-server';

The angular.json needs some corrections as well. First, replace the main.server.ts with server.ts.
Second, in the projects:[your-project-name]:architect add “serve-ssr” and “prerender” below:

        "serve-ssr": {
          "builder": "@nguniversal/builders:ssr-dev-server",
          "options": {
            "browserTarget": "[your-project-name]:build",
            "serverTarget": "[your-project-name]:server"
          },
          "configurations": {
            "production": {
              "browserTarget": "[your-project-name]:build:production",
              "serverTarget": "[your-project-name]:server:production"
            }
          }
        },
        "prerender": {
          "builder": "@nguniversal/builders:prerender",
          "options": {
            "browserTarget": "[your-project-name]:build:production",
            "serverTarget": "[your-project-name]:server:production",
            "routes": [
              "/"
            ]
          },
          "configurations": {
            "production": {}
          }
        }
Make sure you replace the [your-project-name] with the actual name of your project in angular.json.

NativeScript: GitHub Action and Apple App store integration

Are you a windows developer and struggling with XCode build? Good news. GitHub Action will help you build and deploy straight to the Apple app store.

First step: Create a basic GitHub Action.

Step two: Download your Provisioning Profile from the Apple store and place it in some local directory. For  example, ./Mobile/publishing.requirements/iOS/my.mobileprovision

Step three: Download your p12 certificate and place it in the same directory. For example, ./Mobile/publishing.requirements/iOS/Certificates.p12

Step four: Create your “app-specific password”. See: “Tips for Updating NativeScript Apps on App Store and Google Play“, by Rob Lauer.

Step five: Create GitHub secrets. The first, called APPLE_APP_SPECIFIC_PASSWORD for the “app-specific password”, second: APPLE_APP_USER_NAME for Apple store user name and the third APPLE_CERT_P12_PASSWORD for the p12 certificate.

Step six: Find the Provisioning Profile Identifier. This is a GUID and it looks like xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx. I open it with the WordPad and searched for “UUID”

Step seven: Modify GitHub Action yml-file.

Finally, push updates to the master branch. The GitHub Action will put your new version into the Apple App Store.

name: Mobile-IOS-Master 

on:
  push:
    branches:
      - master 

env:
  NODE_VERSION: '12.x' # set this to the node version to use 

jobs:
  build:
    runs-on: macos-latest 

    steps:

    - uses: actions/checkout@v1 
    - name: Cache web node modules 
      uses: actions/cache@v1 
      with: 
        path: ~/.npm 
        key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} 
        restore-keys: | ${{ runner.os }}-node- 

    - name: Use Node.js ${{ env.NODE_VERSION }} 
      uses: actions/setup-node@v1 
      with: 
        node-version: ${{ env.NODE_VERSION }} 
        registry-url: 'https://registry.npmjs.org' 

    - uses: apple-actions/import-codesign-certs@v1 
      with: 
        p12-filepath: './Mobile/publishing.requirements/iOS/Certificates.p12' 
        p12-password: ${{ secrets.APPLE_CERT_P12_PASSWORD }} 

    - name: Provisioning Profiles 
      run: | 
        mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles 
        echo "List profiles" 
        ls ~/Library/MobileDevice/Provisioning\ Profiles/ 
        echo "Move profiles" 
        cp ./Mobile/publishing.requirements/iOS/my.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/ 
        echo "List profiles" 
        ls ~/Library/MobileDevice/Provisioning\ Profiles/ 

    - name: Setup NativeScript 
      run: npm install -g nativescript 

    - name: Install Pip and Six 
      run: | 
        sudo pip install --upgrade pip 
        pip install --user --upgrade matplotlib 
        pip install six 

    - name: Verify the NativeScript setup 
      run: tns doctor 

    - name: Build IOS release
      run: | 
        cd ./Mobile tns build ios --release --for-device --env.production --provision xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx 

    - name: Deploy to App store 
      run: | 
        xcrun altool --upload-app --type ios --file ./Mobile/platforms/ios/build/Release-iphoneos/Mobile.ipa --username ${{ secrets.APPLE_APP_USER_NAME }} --password ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}

Failed to proxy the request to http://localhost:4200/ on Azure App Service

We migrated our Angular 9 open-source SPA app from .NET Core 2.2 to .Net Core 3.1. Everything went well and the app ran fine in the Linux App Service on Azure. When we deployed to Windows App Service, we got an error:

HttpRequestException: Failed to proxy the request to http://localhost:4200/, because the request to the proxy target failed. Check that the proxy target server is running and accepting requests to http://localhost:4200/.

I validated the Azure environment and notice a message: 

Microsoft.Hosting.Lifetime: Hosting environment: Development

I tried to set ASPNETCORE_ENVIRONMENT to “Production“, but I keep getting the “Hosting environment: Development” message in the Log stream.

We are using a “Zip” deployment (WEBSITE_RUN_FROM_PACKAGE). Where the zip file is on Azure storage. After several hours of research, I noticed small changes in the web.config file:

<environmentVariables>
<environmentVariable name="ASPNETCORE_HTTPS_PORT" value="44395" />
<environmentVariable name="COMPLUS_ForceENC" value="1" />
<environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Development" />
</environmentVariables>

Apparently, during the conversion, these entries got into web.config file and they have priority over settings in the Configuration on the Azure portal. 

I changed setting to <environmentVariables /> and the site is running again.

Azure Free SSL Certificate with “Let’s Encrypt”

Recently, I have a chance to read Nik Molnar article [“Let’s Encrypt” Azure Web Apps the Free and Easy Way]. This article provides an easy way to incorporate a free SSL Certificate and automate it renewal.

Several minor issues I have to correct during the installation process.

  1. The tenantId was not displayed on the Azure portal. I used  az login PowerShell command to display TenantId. The simple command:
    $tenantId =  (az login | ConvertFrom-Json)[0].tenantId
    $tenantId
    
  2. Another issue was with the usage of legacy PowerShell Azure command in the “Register a Service Principal” section. I replaced them with the latest “az” CLI commands:
az login
$uri = 'http://' + (New-Guid).Guid
$passwordData='qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM1234567890$!*'
$password = 'p' + ((Get-Random -Count 25 -InputObject ([char[]]$passwordData)) -join '') + 'Z0!'
$app = (az ad app create --display-name Lets-Encrypt-For-Azure-Web-Apps --homepage $uri --identifier-uris $uri --password $password)
$applicationId = ($app | ConvertFrom-Json).appId
az ad sp create --id $applicationId
az role assignment create --role Contributor --assignee $applicationId
$applicationId
$password

Several points:

  1. I used Guid for Uri name of the home page;
  2. I autogenerated random password;
  3. The ApplicationId now called appId.
  4. I converted JSON output using “ConvertFrom-Json

If you are using the “rewrite”, please use the following:

<rule name="LetsEncrypt" patternsyntax="ECMAScript" stopprocessing="true">
          <match url=".well-known/(.*)"></match>
</rule>

Run Azure function with HTTPS endpoint under localhost from Visual Studio

Recently, I worked on Azure function and Angular integration project. The Angular application is using https port on localhost. Call to Azure function on the localhost failed as we have mixed (HTTP and HTTPS) content.

Searching for a solution I found several recommendations to use the direct call:

func host start --port <SSL_PORT> --useHttps --cors * --cert certificate.pfx --password <password>

First, I was not even sure how to run this command within Microsoft Visual Studio. Secondary, where I should get a certificate.

If you install Microsoft .net core, you have a localhost certificate. SCOTT HANSELMAN in his article explains it in details.

Interesting that the certificate installed and bind to all ports from 44300 to 44399. You may use the following DOS command to confirm this:

C:\> netsh http show sslcert 0.0.0.0:44310

The above command should show output similar to one below:

SSL Certificate bindings:
-------------------------
IP:port : 0.0.0.0:44310
Certificate Hash : b1cf45ab3a51bd347bd809580e55bb7d541a3b84
Application ID : {214124cd-d05b-4309-9af9-9caa44b2b23a}
Certificate Store Name : MY
Verify Client Certificate Revocation : Enabled
Verify Revocation Using Cached Client Certificate Only : Disabled
Usage Check : Enabled
Revocation Freshness Time : 0
URL Retrieval Timeout : 0
Ctl Identifier : (null)
Ctl Store Name : (null)
DS Mapper Usage : Disabled
Negotiate Client Certificate : Disabled
Reject Connections : Disabled
Disable HTTP2 : Not Set
Disable QUIC : Not Set
Disable TLS1.3 : Not Set
Disable OCSP Stapling : Not Set

Now I can bind my local azure function to any of the 100 ports.

The first step is to choose a port. For example, 43310.

The second is to add this port to Azure function project. Find, a file called “local.settings.json”. Add the following line:

"Host": {
"LocalHttpPort": 44310
}

Add the following to the property of the azure function project:

host start --pause-on-error --useHttps

host start –pause-on-error –useHttps

Now, you should be able to run the azure function project and verify that it’s hosted on https://localhost:43310/

Note: This solution is working only for V1. For V2 you should add –useDefaultCert parameter and ignore --cert¬†--password. See: https://github.com/Azure/azure-functions-core-tools/pull/1938

Troubleshooting Azure WEBSITE_RUN_FROM_PACKAGE deployment

You may already know about the new Azure Run From Package feature. This is an excellent new feature comparable with Containers. The zip file does not need to be changed when you move from Test environment to Production. Just point your Application Settings to the zip file on your Azure storage.

The problem with this type of deployment is when you are trying to troubleshoot your application code. The web.config now is read-only and cannot be modified without repacking the new ZIP.

The Azure “Log stream” will not help. To output errors into Azure “Log stream” add the following loggers to your code:

public void ConfigureServices(IServiceCollection services)
        {
            services.AddLogging(loggingBuilder =>
            {
                loggingBuilder.AddConfiguration(this.Configuration.GetSection("Logging"));
                loggingBuilder.AddConsole();
                loggingBuilder.AddDebug();
                loggingBuilder.AddAzureWebAppDiagnostics();
            });
...
}

The AddAzureWebAppDiagnostics¬†will output error messages into the Azure “Log stream“. Make sure you enable the “Application Logging” and “Detailed error messages” in the Azure Diagnostics logs.


PM> Install-Package Microsoft.Extensions.Logging.AzureAppServices -Version 2.2.0

Prepare your Angular1 to Angular2 migration

As a Sr. Software Architect, currently I’m working on very large Angular 1 project. We have over hundred controllers, services, directives and components. Our goal is to convert the project into Angular2 in order to use pre-rendering techniques. Unfortunately, we cannot stop current development and spend time to massive conversion. ¬†We have many feature that we need to implement asap.

In order to simplify conversion we adopted several strategies:

  1. We converted our JavaScript files to Typescript. The compilation and types bring immediately code quality benefits. We discovered several bugs that we missed during development and testing.
  2. We are starting to use Angular 1.5 components for every new form.

Continue reading