/minifier

A URL minifier which works with Azure Functions

Primary LanguageBicepMIT LicenseMIT

URL Minifier in Azure

Build status
Build and deploy solution

A URL minifier which works with Azure Functions, and a couple of other Azure services, just because we can!

Application

Usage

Supported actions

Action HTTP VERB Description
Get GET Retrieves the URL matching the slug and redirects, or returns a NotFound code (404).
Create POST Creates a new slug in the repository, along with the redirect URL. The response is either BadRequest (400) or Created (201)
Delete DELETE Removes the URL matching the specified slug from the repository. The response is either BadRequest (400), Ok (200) if the deletion is done, or ExpectationFailed (417) if anything forbid the deletion to be performed.

GET

Invoke the Azure Function like this.

GET http://{yourInstance}/someSlug

Replace {yourInstance} with your actual host.

POST

POST http://localhost:7071/api/Create?code=yourFunctionKey
{
    "slug": "blog",
    "url": "https://jan-v.nl"
}

This will result in the slug being stored in the repository. Make sure you have some authorization in place, like using a Function Key.

DELETE

Not implemented, yet

DELETE http://localhost:7071/api/Delete?code=yourFunctionKey
{
    "slug": "blog",
}

This will remove the specified slug from the repository. Make sure you have some authorization in place, like using a Function Key.

Configuration

There are several configuration values the solution depends upon.

Local development - local.settings.json

You should add a file called local.settings.json, if you want to run the solution yourself.

While the deployment templates make sure the appropriate permissions are set for the managed identities, you need to do this for yourself when running on your own machine. Within Visual Studio you can set the used identity in Tools -> Options -> Azure Service Authentication -> Account Selection. Most other tools use the identity configured via the Azure CLI.

The Azure Functions are using identity-based bindings, therefore you need to grant yourself the appropriate roles to use these. You can use the following script to grant yourself the appropriate roles for Cosmos DB.

$resourceGroupName='<myResourceGroup>'
$accountName='<myCosmosAccount>'
# Cosmos DB Built-in Data Reader: 00000000-0000-0000-0000-000000000001
# Cosmos DB Built-in Data Contributor: 00000000-0000-0000-0000-000000000002
$readOnlyRoleDefinitionId = '<roleDefinitionId>'
$principalId = '<aadPrincipalIdOfYourManagedIdentity>'
az cosmosdb sql role assignment create --account-name $accountName --resource-group $resourceGroupName --scope "/" --principal-id $principalId --role-definition-id $readOnlyRoleDefinitionId

The principalId is the Object Id of your Managed Identity OR from your own Azure Active Directory account.

The contents of the Minifier.Backend project should look similar to the following:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet",
    "MinifierIncomingMessages__fullyQualifiedNamespace": "{yourServiceBusNamespaceName}.servicebus.windows.net",
    "IncomingUrlsTopicName": "incoming-minified-urls", // This one is defined in the Bicep template, but you can change it if you want.
    "IncomingUrlsProcessingSubscription": "process", // This one is defined in the Bicep template, but you can change it if you want.
    "UrlMinifierRepository__accountEndpoint": "https://{yourCosmosDbAccountName}.documents.azure.com:443/",
    // Or this if you're running with the local emulator
    "UrlMinifierRepository": "AccountEndpoint=https://localhost:8081/;AccountKey={theEmulatorKey}",
    "UrlMinifierRepository__DatabaseName": "minifier", // This one is defined in the Bicep template, but you can change it if you want.
    "UrlMinifierRepository__CollectionName": "urls" // This one is defined in the Bicep template, but you can change it if you want.
  }
}

The contents of the Minifier.Frontend project should look similar to the following:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet",
    "UrlMinifierRepository__accountEndpoint": "https://{yourCosmosDbAccountName}.documents.azure.com:443/",
    "UrlMinifierRepository__DatabaseName": "minifier", // This one is defined in the Bicep template, but you can change it if you want.
    "UrlMinifierRepository__CollectionName": "urls", // This one is defined in the Bicep template, but you can change it if you want.
    "MinifierIncomingMessages__fullyQualifiedNamespace": "{yourServiceBusNamespaceName}.servicebus.windows.net",
    "IncomingUrlsTopicName": "incoming-minified-urls", // This one is defined in the Bicep template, but you can change it if you want.
    "IncomingUrlsProcessingSubscription": "updatefrontendweu" // This one is defined in the Bicep template, but you can change it if you want.
  }
}

To get the necessary Azure resources, run the following Azure CLI commands in the folder ./deployment/infrastructure/:

az deployment sub create --location WestEurope --template-file basic-infrastructure.bicep --parameters parameters.lcl.json

az deployment sub create --location WestEurope --template-file main.bicep --parameters parameters.lcl.json

In Azure

This is all being handled by the Bicep template in this repository, so no need to worry about it.

Deployment

To deploy this solution you need to create a service principal in Azure which has the appropriate roles to create resource groups, all resources and set permissions (RBAC) to all these resources. The easiest way to set this up is by using the Owner role, as it has enough permissions to apply roles. However, keep in mind, this grants the service principal a lot of power on the entire subscription.

az ad sp create-for-rbac --name "minifier" --role owner --scopes /subscriptions/{subscriptionId} --sdk-auth

What I'm doing to limit this is to make this service principal a Contributor, which still grants it a lot of power, and applying the Owner role to the created resource group after the first (failed) deployment.

After executing the command above, you should have an output similar to the following:

{
  "clientId": "d9816ed2-66f2-40f2-bcf0-1b222a910416",
  "clientSecret": "someSuperSecret",
  "subscriptionId": "fe2a1369-754c-4703-ba37-c3a864e1eac8",
  "tenantId": "b491f32d-ecc5-43ba-9699-0860994360d7",
  "activeDirectoryEndpointUrl": "https://login.microsoftonline.com",
  "resourceManagerEndpointUrl": "https://management.azure.com/",
  "activeDirectoryGraphResourceId": "https://graph.windows.net/",
  "sqlManagementEndpointUrl": "https://management.core.windows.net:8443/",
  "galleryEndpointUrl": "https://gallery.azure.com/",
  "managementEndpointUrl": "https://management.core.windows.net/"
}

Store this value in a GitHub secret called AZURE_DEV and you're good to go!

Additional features

The basics of the URL minfier are covered above. The following contents will be about additional features which are totally optional to add. During deployment the toggle additionalFeatures can be set to either true or false depending on if you want to have this deployed too.

Summarize contents of a page

An API call can be made to the summarize endpoint, using a slug.

GET https://{host}/summarize/{slug}

This wil result in the following response:

{
    "summary": "lorem ipsum..."
}

The summary is created using Azure Open AI service with a specified LLM in the configuration.

The following settings are necessary, along with a deployed Azure Open AI service and model.

{
    "OpenAiServiceCompletionEndpoint": "",
    "OpenAiServiceKey": "",
    "OpenAiServiceDeploymentId": "",
}

Note: At this moment in time, only specific Azure subscriptions can deploy the Azure Open AI service. While this repository contains the Bicep template for it, the service is deployed in a different subscription. Therefore, the settings necessary will be injected via GitHub secrets to the deployment.