Enterprise Framework

Software Solutions in the Enterprise

Create PFX from Azure App Service Certificate Service

I recently created a Azure App Service Certificate that I wanted to use with Azure Application Gateway.  However, this requires you to upload an PFX file and there isn't an option to generate one from Azure App Service Certificate.  However, I found an article that can generate the PFX for you from the App Service Certificate;

https://blogs.msdn.microsoft.com/appserviceteam/2017/02/24/creating-a-local-pfx-copy-of-app-service-certificate/

The step by step I had to do:

1.  Verify new certificate was valid and not expired

2.  Install the Azure PowerShell commandlets. Install instructions from https://docs.microsoft.com/en-us/powershell/azure/install-azurerm-ps?view=azurermps-6.7.0&viewFallbackFrom=azurermps-6.2.0

Open Powershell ISE as Administrator

4.  Run:  Install-Module -Name AzureRM

Sign into Azure

# Import the module into the PowerShell session

5.  Run:  Import-Module AzureRM

# Connect to Azure with an interactive dialog for sign-in

6.  Run:  Connect-AzureRmAccount

Paste the below into the Powershell Window

Function Export-AppServiceCertificate
{
###########################################################

Param(
[Parameter(Mandatory=$true,Position=1,HelpMessage="ARM Login Url")]
[string]$loginId,

[Parameter(Mandatory=$true,HelpMessage="Subscription Id")]
[string]$subscriptionId,

[Parameter(Mandatory=$true,HelpMessage="Resource Group Name")]
[string]$resourceGroupName,

[Parameter(Mandatory=$true,HelpMessage="Name of the App Service Certificate Resource")]
[string]$name
)

###########################################################

Login-AzureRmAccount
Set-AzureRmContext -SubscriptionId $subscriptionId

## Get the KeyVault Resource Url and KeyVault Secret Name were the certificate is stored
$ascResource= Get-AzureRmResource -ResourceId "/subscriptions/$subscriptionId/resourceGroups/$resourceGroupName/providers/Microsoft.CertificateRegistration/certificateOrders/$name"
$certProps = Get-Member -InputObject $ascResource.Properties.certificates[0] -MemberType NoteProperty
$certificateName = $certProps[0].Name
$keyVaultId = $ascResource.Properties.certificates[0].$certificateName.KeyVaultId
$keyVaultSecretName = $ascResource.Properties.certificates[0].$certificateName.KeyVaultSecretName

## Split the resource URL of KeyVault and get KeyVaultName and KeyVaultResourceGroupName
$keyVaultIdParts = $keyVaultId.Split("/")
$keyVaultName = $keyVaultIdParts[$keyVaultIdParts.Length - 1]
$keyVaultResourceGroupName = $keyVaultIdParts[$keyVaultIdParts.Length - 5]

## --- !! NOTE !! ----
## Only users who can set the access policy and has the the right RBAC permissions can set the access policy on KeyVault, if the command fails contact the owner of the KeyVault
Set-AzureRmKeyVaultAccessPolicy -ResourceGroupName $keyVaultResourceGroupName -VaultName $keyVaultName -UserPrincipalName $loginId -PermissionsToSecrets get
Write-Host "Get Secret Access to account $loginId has been granted from the KeyVault, please check and remove the policy after exporting the certificate"

## Getting the secret from the KeyVault
$secret = Get-AzureKeyVaultSecret -VaultName $keyVaultName -Name $keyVaultSecretName
$pfxCertObject= New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList @([Convert]::FromBase64String($secret.SecretValueText),"",[System.Security.Cryptography.X509Certificates.X509KeyStorageFlags]::Exportable)
$pfxPassword = -join ((65..90) + (97..122) + (48..57) | Get-Random -Count 50 | % {[char]$_})
$currentDirectory = (Get-Location -PSProvider FileSystem).ProviderPath
[Environment]::CurrentDirectory = (Get-Location -PSProvider FileSystem).ProviderPath
[io.file]::WriteAllBytes(".\appservicecertificate.pfx",$pfxCertObject.Export([System.Security.Cryptography.X509Certificates.X509ContentType]::Pkcs12,$pfxPassword))

## --- !! NOTE !! ----
## Remove the Access Policy required for exporting the certificate once you have exported the certificate to prevent giving the account prolonged access to the KeyVault
## The account will be completely removed from KeyVault access policy and will prevent to account from accessing any keys/secrets/certificates on the KeyVault, 
## Run the following command if you are sure that the account is not used for any other access on the KeyVault or login to the portal and change the access policy accordingly.
# Remove-AzureRmKeyVaultAccessPolicy -ResourceGroupName $keyVaultResourceGroupName -VaultName $keyVaultName -UserPrincipalName $loginId
# Write-Host "Access to account $loginId has been removed from the KeyVault"

# Print the password for the exported certificate
Write-Host "Created an App Service Certificate copy at: $currentDirectory\appservicecertificate.pfx"
Write-Warning "For security reasons, do not store the PFX password. Use it directly from the console as required."
Write-Host "PFX password: $pfxPassword"
}

Then execute the following:

Export-AppServiceCertificate -loginId youremail@domain.com -subscriptionId ########-####-####-####-############ -resourceGroupName YourCertResourceGroupName -name NameOfCertificate
 


Mac Sierra : Git Completion and Bash Prompt With Branch Update

The following will help get GIT completion setup locally on Mac Sierra.  Not correctly loading the git-completion.bash or git-prompt.sh can result in getting the following error: 


ERROR:   __git_ps1: command not found


Check that you did not mistype the file name as that could result in the script not being found and ran.


STEPS:

$ cd ~/Downloads       

$ curl -OL https://github.com/git/git/raw/master/contrib/completion/git-completion.bash

$ mv git-completion.bash ~/.git-completion.bash

$ sudo nano ~/.bashrc

ADD .bashrc CONTENTS
===============================
 
if [ -f ~/.git-completion.bash ]; then
    source ~/.git-completion.bash
fi
 


$ cd ~/Downloads/

$ curl -OL https://raw.githubusercontent.com/git/git/master/contrib/completion/git-prompt.sh
 
$ mv ~/Downloads/git-prompt.sh ~/.git-prompt.sh

 
UPDATE .bash_profile CONTENTS
===============================
 
if [ -f ~/.git-prompt.sh ]; then
    source ~/.git-prompt.sh
fi
 
export PS1='\h:\u \W$(__git_ps1 "(%s)") $ '

Error: Microsoft Graph Development connecting to Outlook API returns Sign In Trouble Bad Request

After registering your app in https://apps.dev.microsoft.com/ and trying to connect to the Microsoft Outlook API using Microsoft Graph, you get the following Error:

Sign In
Sorry, but we’re having trouble signing you in.

We received a bad request.


CODE : THINGS TO CHECK

Make sure your API key and Password are correct in your app code.

API : THINGS TO CHECK

  1. Go to the Application Registration Portal at https://apps.dev.microsoft.com/
  2. Select your application
  3. Add your local development urls, 
    1. http://localhost
    2. http://localhost:8080
    3. https://localhost:44300
Make sure the redirect being passed from the code to Graph is passing a Url registered in the Application Registration Portal.  If successful, you should see:

App publisher website: localhost




Error : Entity Framework : Update-Database

Error : Entity Framework : Update-Database

PM> Update-Database -ConnectionString "data source=.;Integrated Security=true;initial catalog=my_db" -ConnectionProviderName "System.Data.SqlClient" -Verbose

Using StartUp project 'Integration Services Project1'.

Using NuGet project 'MyDataProject'.

Exception calling "SetData" with "2" argument(s): "Type 'Microsoft.DataWarehouse.VsIntegration.Shell.Project.Extensibility.ProjectExt' in assembly 'Microsoft.DataWarehouse.VsIntegration, 

Version=13.0.0.0, Culture=neutral, PublicKeyToken=89845dcd8080cc91' is not marked as serializable."

At C:\Source\Repos\MyApplication\packages\EntityFramework.6.1.3\tools\EntityFramework.psm1:720 char:5

+     $domain.SetData('startUpProject', $startUpProject)

+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException

    + FullyQualifiedErrorId : SerializationException

 System.NullReferenceException: Object reference not set to an instance of an object.

   at System.Data.Entity.Migrations.Extensions.ProjectExtensions.GetProjectTypes(Project project, Int32 shellVersion)

   at System.Data.Entity.Migrations.Extensions.ProjectExtensions.IsWebProject(Project project)

   at System.Data.Entity.Migrations.MigrationsDomainCommand.GetFacade(String configurationTypeName, Boolean useContextWorkingDirectory)

   at System.Data.Entity.Migrations.UpdateDatabaseCommand.<>c__DisplayClass2.<.ctor>b__0()

   at System.Data.Entity.Migrations.MigrationsDomainCommand.Execute(Action command)

Object reference not set to an instance of an object.


How to fix:

  1. In Package Manager Console, make sure "Default Project" is set to the project that has Data Migrations enabled
  2. Make sure the Web Project is set as the Start project (I know crazy)
  3. Make sure the database connection string is correct.



Error : Sitefinity Thunder Compile Error

Error:

Severity    Code    Description    Project    File    Line    Suppression State
Error    CS0433    The type 'DatabaseGeneratedOption' exists in both 'EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' and 'System.ComponentModel.DataAnnotations, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'    MyApplications    C:\Source\Repos\MyApplications\Models\ApplicationType.cs    98    Active

Fix:

The generated Sitefinity Assembly has a different Target .NET Framework.

Right Click a Project > Choose Properties > Ensure all Target Framework versions are the same.  (Example 4.5)

How To : NuGet Best Practices for Adding To New or Existing Projects

How To : NuGet Best Practices For Adding to New or Existing Projects

Keeping Nuget packages consistent in your solution is critical.  A lot of devs even now and days do it the hard way by going to the individual projects and trying to manage the Packages that way.  Managing NuGet packages in your solutions is easier than you think it is and will reduce headaches of DLL hell.

Packages in Source Control

  • DO NOT put NuGet packages into Source Control.  Exclude the Packages folder from the Solution root before checking in.  Whenever your solution is pulled fresh from source control, it will determine the packages don't exist and try to pull them down automatically if they are from the public NuGet servers.
  • DO NOT distribute packages with your code unless they are from your internal NuGet server and you are giving it to a client or something.
Using Visual Studio 2015

  • Right click the Solution > Choose Manage NuGet Packages for Solution.
  • Click Installed, this will show you all of the NuGet packages in the left column currently used by all projects in the solution.  The right column displays a list of the projects and the NuGet package version that is installed next to it.  
  • In the left column, choose a NuGet Package (Example:  Newtonsoft.Json)   
  • In the right column in the list of projects, you should scroll down and ensure that all the version numbers are the same across all projects, if they are not, you really should update them.  You can do this by putting a check next to the project with a older NuGet version.  Then in Version row, choose the one that matches the other projects in the solutions and click, 
NOTE:  Before updating packages you should finish any development work you are on before messing with NuGet Packages.  

Summary:  
  • DO NOT include the NuGet Packages folder in source control.  
  • DO NOT distribute NuGet Packages unless from internal NuGet server and you need to.
  • Use Manage NuGet Packages for solution to determine which NuGet packages and versions are being used across projects.  
  • Sync NuGet package versions across projects (Checkin Dev work before messing with Packages).


NopCommerce Plugin Best Practices

NopCommerce Plugin Best Practices

NOTE:  DRAFT IN PROGRESS

Versions:    3.7

What are NopCommerce Plugins?

  • NopCommerce plugin are components that can extend the core NopCommerce E-Commerce platform.
  • NopCommerce Plugins can be used to add, extend or replace features within NopCommerce
  • NopCommerce Plugins are mini MVC components, it can use Controllers, Views, Routes, Models, and JavaScript that you would typically include within a ASP.NET MVC App.
  • NopCommerce Plugins are registered within NopCommerce dynamically and then installed through configuration for use within your NopCommerce application.

What types of NopCommerce Plugins can you create?

  • Admin Plugin - Allow you create new screens for use on the Admin page or even replace existing Admin features.
  • Retail Product Plugin - Allows you create new screens or overwrite existing retail product page features
  • Checkout Plugin - Allows you add to checkout or replace existing checkout.
  • Web Service Plugin - Create a RESTful Web API end point.
  • Mixed plugin - A combination of plugins for use in your NopCommerce site.
  • more...

What are the advantages of using a Plugin
  • A isolated set of features that are distribute-able (Assuming you kept references clean)
  • A isolated set of features that aren't impacted by a NopCommerce upgrade (hopefully)
  • You can replace NopCommerce endpoint calls and replace it with a custom one in your plugin.
  • Easy way to add non-core tables to the system

What are the dis-advantages of using a Plugin

  • Replacing the Checkout process with a plugin is probably not the greatest idea.
  • No Referential Integrity against existing Nop tables
  • Plugins that intercept core NopCommerce endpoints can become a maintenance nightmare.
 
Custom Plugin development challenges. 

You should follow these rules to try and minimize work

  1. Use the same Nuget package versions across all projects.  Follow NuGet Best Practices.  By not managing NuGet Packages correct could result in weird behaviors in your application.
  2. DO NOT checkin the NuGet package into source control.
  3. No not directly reference DLL's in the Packges folder, use the NuGet to include
  4. Your plugin should encapsulate all the logic required to operate
  5. Minimize changes to the core NopCommerce code base
  6. Controllers should be responsible for hydrating the current session/request and then passing that information down to helper services.
  7. Controllers should not contain your business logic because it's hard to test
  8. Services that contain logic should not be aware of HTTP Context and Session.
  9. Minimize size of methods (SOLID principle)
  10. Using a Plugin to override the Checkout Workflow is a complex task and should be a last resort.

Custom Plugin Endpoints

  • Do NOT start custom route End Points with the word Admin
routes.MapRoute("Plugins.MyCompany.Admin.Product.List",
     "Admin/MyPlugin/Product/List",
     new { controller = "MyProduct", action = "Admin" },
     new[] { "MyCompany.Plugin.Misc.PluginName.Controllers" }
);
  • Start custom routes End Points with the word Plugins
routes.MapRoute("Plugins.MyCompany.Admin.Product.List",
     "Plugins/MyPlugin/Product/List",
     new { controller = "MyProduct", action = "Admin" },
     new[] { "MyCompany.Plugin.Misc.PluginName.Controllers" }
);

Base Plugin Folder and File Structure

  • \Content
  • \Controllers
  • \Extensions
  • \Infrastructure
  • \Models
  • \Scripts
  • \Services
  • \Validators
  • \Views
  • \Sql
  • Description.txt
  • Logo.png
  • Notes.Txt
  • packages.config
  • RouteProvider.cs

Routes



Resources

How To : NopCommerce Best Practices for Order table

How To:  NopCommerce Best Practices for Order table

NopCommerce Versions:  3.7


NOTE:  DRAFT IN PROGRESS


Description:

The Order table captures both registered Customers and Guest orders.  

Registered Customer Orders will use the Customer.Id associated to them at registration.  During checkout, users are prompted for the billing and shipping address.  The billing and shipping Address records are duplicated as point in time records and associated to the Order.

Guest Checkout Orders orders will leave a cookie on the client with a generated ID that was created in the Customer table.  The Next time the registered Guest user comes to the site and places an order as Guest, it will use the originally created Customer.Id to use and associate to the Order.  If the user decides to register at this point, it will use the originally created Customer record associated to the cookie.ID to be used for the newly registered account.

You will find that any time an order is placed, a point in time copy of a billing and shipping address record is created and associated to the order.  This allows the order to have historical records at that point in time.  The created address will not by modified again.

Table:  Order

  • AffiliateId (int, 4)
  • AllowStoringCreditCardNumber (bit, 1)
  • AuthorizationTransactionCode (nvarchar, not null)
  • AuthorizationTransactionId (nvarchar, not null)
  • AuthorizationTransactionResult (nvarchar, not null)
  • BillingAddressId (int, 4)
  • CaptureTransactionId (nvarchar, not null)
  • CaptureTransactionResult (nvarchar, not null)
  • CardCvv2 (nvarchar, not null)
  • CardExpirationMonth (nvarchar, not null)
  • CardExpirationYear (nvarchar, not null)
  • CardName (nvarchar, not null)
  • CardNumber (nvarchar, not null)
  • CardType (nvarchar, not null)
  • CheckoutAttributeDescription (nvarchar, not null)
  • CheckoutAttributesXml (nvarchar, not null)
  • CreatedOnUtc (datetime, 8)
  • CurrencyRate (decimal, 9)
  • CustomerCurrencyCode (nvarchar, not null)
  • CustomerId (int, 4)
  • CustomerIp (nvarchar, not null)
  • CustomerLanguageId (int, 4)
  • CustomerTaxDisplayTypeId (int, 4)
  • CustomValuesXml (nvarchar, not null)
  • Deleted (bit, 1)
  • ForEndCustomerCompanyAddressId (int, 4, not null)
  • ForEndCustomerCompanyId (int, 4, not null)
  • ForResellerCompanyAddressId (int, 4, not null)
  • ForResellerCompanyId (int, 4, not null)
  • ForResellerCompanyTypeId (int, 4, not null)
  • Id (PK, int, 4)
  • IsTransferable (bit, 1)
  • MaskedCreditCardNumber (nvarchar, not null)
  • OrderDiscount (decimal, 9)
  • OrderGuid (uniqueidentifier, 16)
  • OrderShippingExclTax (decimal, 9)
  • OrderShippingInclTax (decimal, 9)
  • OrderStatusId (int, 4)
  • OrderSubTotalDiscountExclTax (decimal, 9)
  • OrderSubTotalDiscountInclTax (decimal, 9)
  • OrderSubtotalExclTax (decimal, 9)
  • OrderSubtotalInclTax (decimal, 9)
  • OrderTax (decimal, 9)
  • OrderTotal (decimal, 9)
  • PaidDateUtc (datetime, 8, not null)
  • PaymentMethodAdditionalFeeExclTax (decimal, 9)
  • PaymentMethodAdditionalFeeInclTax (decimal, 9)
  • PaymentMethodSystemName (nvarchar, not null) - This is the name of the plugin system name used to do the payment.  Example:  Payments.PurchaseOrder (Nop.Plugin.Payments.Manual), Payments.Manual (Nop.Plugin.Payments.Manual). 
  • PaymentStatusId (int, 4)
  • PickUpInStore (bit, 1)
  • RefundedAmount (decimal, 9)
  • RewardPointsWereAdded (bit, 1)
  • ShippingAddressId (int, 4, not null)
  • ShippingMethod (nvarchar, not null)
  • ShippingRateComputationMethodSystemName (nvarchar, not null)
  • ShippingStatusId (int, 4)
  • StoreId (int, 4)
  • SubscriptionTransactionId (nvarchar, not null)
  • TaxRates (nvarchar, not null)
  • VatNumber (nvarchar, not null)

Related Tables:

  • OrderItem
  • OrderNote
  • GenericAttribute

Uses:

  • Check Out > Registered User: 
  • Checkout > Guest:
  • Admin > Order: 
Extending:




How To : NopCommerce Best Practices for Address table

How To:  NopCommerce Best Practices for Address table

NopCommerce Versions:  3.7


Note:  This is a draft version in progress.

Description:  The Address table is used for capturing Address information for Registered Customers, Guest Checkout Customers and Orders.  

  • The best way to think about the Address table is that it has multi-state data meaning the Address data can be point in time created and associated with an Order and won't change again or current data that represents the current Address of a customer. Every time an order is created a snapshot point in time Address record is created (Duplicated) for the Billing Address and Shipping Address.

Table:  Address

  • Address1 (nvarchar, not null)
  • Address2 (nvarchar, not null)
  • City (nvarchar, not null)
  • Company (nvarchar, not null)
  • CompanyId (int, 4, not null)
  • CountryId (int, 4, not null)
  • CreatedOnUtc (datetime, 8)
  • CustomAttributes (nvarchar, not null)
  • Email (nvarchar, not null)
  • FaxNumber (nvarchar, not null)
  • FirstName (nvarchar, not null)
  • Id (PK, int, 4)
  • LastName (nvarchar, not null)
  • PhoneNumber (nvarchar, not null)
  • StateProvinceId (int, 4, not null)
  • ZipPostalCode (nvarchar, not null)

Related Tables:

  • AddressAttribute
  • AddressAttributeValue
  • CustomerAddresses

Uses:

  • Retail Site > My Account > Addresses
  • Customer Entity > Checkout
  • Admin > Customer > Addresses
Extending:

  • The Address table already contains an extensive common set of information relating to addresses.  Instead of modifying the table, you should add a table that has a foreign key to the Address table but is also the primary key.

How To : NopCommerce Best Practices for Customer table

How To:  NopCommerce Best Practices for Customer table

NopCommerce Versions:  3.7


NOTE:  DRAFT IN PROGRESS

Description:

The Customer table is used for capturing both registered and non registered users of the system.  For registered users, it only contains a small set of information usch as Username, Email, and password as shown below. Additional information relating to the customer such as First Name, Last Name and Gender is captured in the GenericAttribute table.  The different way Customer information is entered are:

  • When a user goes through the Retail > Register process a record is created in the Customer table.    
  • When a user does a Checkout > Guest Checkout (not logged in) they will have a generic empty record added.  The Username, Email, Password, PasswordSalt, AdminContent, SystemName, LastLoginDateutc fields are set as NULL.  The Id, CustomerGuid, PasswordFormatId, IsTaxExempt, AffiliateId, VendorId, HasShoppingCartItems, Active, Deleted, IsSystemAccount, LastIpAddress, CreatedOnUtc, LastActivityDateUtc, BillingAddress_id, ShippingAddress_Id are set.  New Billing and Shipping Address records are created and then associated to the new Customer record. 
  • Guest Checkout Orders orders will leave a cookie on the client with a generated ID that was created in the Customer table.  The Next time the registered Guest user comes to the site and places an order as Guest, it will use the originally created Customer.Id to use and associate to the Order.  If the user decides to register at this point, it will use the originally created Customer record associated to the ID to be used for the new registered to the account.
  • Admins going through Administration > Customer > New and

Table:  Customer

  • Active (bit, 1)
  • AdminComment (nvarchar, not null)
  • AffiliateId (int, 4)
  • BillingAddress_Id (int, 4, not null)
  • CompanyId (int, 4, not null)
  • CreatedOnUtc (datetime, 8)
  • CustomerGuid (uniqueidentifier, 16)
  • CustomerTypeId (int, 4, not null)
  • Deleted (bit, 1)
  • Email (nvarchar, 2000, not null)
  • HasShoppingCartItems (bit, 1)
  • Id (PK, int, 4)
  • IsSystemAccount (bit, 1)
  • IsTaxExempt (bit, 1)
  • LastActivityDateUtc (datetime, 8)
  • LastIpAddress (nvarchar, not null)
  • LastLoginDateUtc (datetime, 8, not null)
  • Password (nvarchar, not null)
  • PasswordFormatId (int, 4)
  • PasswordSalt (nvarchar, not null)
  • ShippingAddress_Id (int, 4, not null)
  • SystemName (nvarchar, 800, not null)
  • Username (nvarchar, 2000, not null)
  • VendorId (int, 4)

Related Tables:
  • Customer_CustomerRole_Mapping
  • CustomerAddressess
  • CustomerAttribute
  • CustomerAttributeValue
  • CustomerRole
  • CustomerType
Uses:

  • Retail Site > Register:
  • Checkout > Guest:
  • Admin > Customer:
Extending:
  • If you are need to capture information relating to the user, you can capture information in the GenericAttribute table, but beware of the GenericAttribute tables usages.  WARNING:  storing data in here does not enforce referential integrity.  You might want to consider the below option of creating a new table with foreign keys if you need data integrity.
  • If you need to enforce referential integrity, instead of adding columns to the Customer table, add a second Table with the foreign key to the Customer table and set it as the Primary Key and then add your custom columns to that new table.