diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index aabb0a6e..473a11b9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,7 +28,7 @@ jobs: - name: Install NodeJs uses: actions/setup-node@v4 with: - node-version: ${{NODEJS_VERSION}} + node-version: ${{env.NODEJS_VERSION}} - name: Restore dependencies run: dotnet restore "${{env.SOLUTION_PATH}}" - name: Build (Backend) for Azure Testing @@ -61,7 +61,7 @@ jobs: - name: Install NodeJs uses: actions/setup-node@v4 with: - node-version: ${{NODEJS_VERSION}} + node-version: ${{env.NODEJS_VERSION}} - name: Restore dependencies run: dotnet restore "${{env.SOLUTION_PATH}}" - name: Build for CI Testing (for Azure) diff --git a/.github/workflows/deploy-azure.yml b/.github/workflows/deploy-azure.yml index 4fc642ce..3eca9ff9 100644 --- a/.github/workflows/deploy-azure.yml +++ b/.github/workflows/deploy-azure.yml @@ -29,7 +29,7 @@ jobs: - name: Install NodeJs uses: actions/setup-node@v4 with: - node-version: ${{NODEJS_VERSION}} + node-version: ${{env.NODEJS_VERSION}} - name: Restore dependencies run: dotnet restore "${{env.SOLUTION_PATH}}" - name: Build Custom GitHub Actions @@ -97,7 +97,7 @@ jobs: - name: Deploy ApiHost1 uses: azure/webapps-deploy@v3 with: - app-name: "${{ vars.DEPLOY_APIHOST1_APP_NAME }}" + app-name: "${{ vars.DEPLOY_AZURE_APIHOST1_APP_NAME }}" package: ./ApiHost1 - name: Download WebsiteHost Artifacts @@ -108,7 +108,7 @@ jobs: - name: Deploy WebsiteHost uses: azure/webapps-deploy@v3 with: - app-name: "${{ vars.DEPLOY_WEBSITEHOST_APP_NAME }}" + app-name: "${{ vars.DEPLOY_AZURE_WEBSITEHOST_APP_NAME }}" package: ./WebsiteHost - name: Download AzureFunctions.Api.WorkerHost Artifacts @@ -119,7 +119,7 @@ jobs: - name: Deploy AzureFunctions.Api.WorkerHost uses: azure/webapps-deploy@v3 with: - app-name: "${{ vars.DEPLOY_AZUREFUNCTIONS_APP_NAME }}" + app-name: "${{ vars.DEPLOY_AZURE_AZUREFUNCTIONS_APP_NAME }}" package: ./AzureFunctions.Api.WorkerHost - name: logout diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md index d8b91f02..aae0c83e 100644 --- a/docs/DEPLOYMENT.md +++ b/docs/DEPLOYMENT.md @@ -22,43 +22,47 @@ This deployment process is defined in the following files: Essentially we are deploying the following pieces of infrastructure in the cloud: * Azure: - * An Azure App Service for each API Host (i.e., `ApiHost`) for the Backend APIs. - * An Azure App Service for the `WebsiteHost` for the Frontend website. - * An Azure FunctionsHost for the functions that monitor queues and message buses. - * An Azure Service Bus for publishing domain_events to various registered subscribers (i.e., `ApiHost1`). + * An Azure "App Service" for each API Host (i.e., `ApiHost`) for the Backend APIs. + * An Azure "App Service" for the `WebsiteHost` for the Frontend website. + * An Azure "Functions Host" for the `AzureFunctions.Api.WorkerHost` that contains functions that monitor queues and message buses. + * An Azure Service Bus for publishing `domain_events` to various registered subscribers (i.e., `ApiHost1`). * An Azure Storage Account for its Queues and Blob storage. * AWS: - * An AWS Lambda function for each API Host (i.e., `ApiHost`) for the Backend APIs. - * An AWS Lambda function for the `WebsiteHost` for the Frontend website. - * An AWS Lambda function, and Queues for each of the Lambdas that monitor queues and message buses. - * An AWS SNS for publishing domain_events to various registered subscribers (i.e., `ApiHost1`). - * An AWS S3 bucket for Blob storage. + * An AWS "Lambda" for each API Host (i.e., `ApiHost`) for the Backend APIs. + * An AWS "Lambda" for the `WebsiteHost` for the Frontend website. + * An AWS "Lambda" for the `AWSLambdas.Api.WorkerHost` with Queues for each of the Lambdas that monitor queues and message buses. + * An AWS "SNS" for publishing `domain_events` to various registered subscribers (i.e., `ApiHost1`). + * An AWS "S3 bucket" for Blob storage. To deploy any of these services to the respective cloud provider, we will use standard GitHub Actions to perform the deployment, available from the [GitHub Actions Marketplace](https://github.com/marketplace?type=actions). ## Configuration -Each one of these deployable pieces of infrastructure will likely require production specific configuration, using variables/secrets that must be defined outside the source code.One safe place to store them is in the GitHub repository, categorized for a specific "environment". +Each one of the deployable pieces of software to this infrastructure will likely require production specific configuration, using variables/secrets that must be defined outside the source code. One such safe place, to store them, is in the GitHub repository, targeted to a specific deployment "environment". We define a deployment "environment" in the GitHub project first, then we define the variables and secrets in that specific environment. -> Unlike, Variables in GitHub (which are only available in a specific environment), Secrets in GitHub can be defined at the organization level, or at the repository level or at the environment level. When reading Secrets, they are combined from Organization, Repository and Environment when read from any GitHub Actions. +> Unlike, variables in GitHub (which are only available to a specific environment), secrets in GitHub can be defined at the organization level, or at the repository level, or at the environment level. When reading secrets from GitHub, they are combined from Organization, Repository and Environment when accessed from any GitHub action. -The initial list of those variables and secrets is detailed in the `appsettings.json` files of each of the deployable hosts in the solution. +The list of the settings is defined in the `appsettings.json` files of each of the deployable hosts in the solution. -Some of these settings have default values already in `appsettings.json`. +Some of these settings have default values already in `appsettings.json`, and some will mandate that they are re-written to target a specific deployment environment (i.e., Production). Rather than defining those secrets/variables in adjacent `appsettings.json` files for specific environments e.g., `appsettings.Production.json` or in ASPNET secrets files stored in source control, or on the local disk, we have decided on a different scheme. -Some of these settings will be "required" to be overwritten before being deployed into a production environment, with production environment settings. These settings will not be known at development time, and MUST NEVER be defined in the source code. +In this scheme, the value of these specific settings will not be known at development time, and MUST NEVER be defined in the source code. However, knowing that a specific setting is required to be re-written in production, is acceptable. -Since, adding new configuration settings is common as products evolve, we need a reliable way to mark up settings as being necessarily "required" to be overwritten in a production environment. As opposed to using the default values already defined in the `appsettings.json` files. +Since, adding new configuration settings is common as products evolve, we need a reliable way to mark up settings as being necessarily "required" to be overwritten in a production environment. -To this end, in the `appsettings.json` file, there is section: `Deploy -> Required -> Keys` which declares the settings that MUST be overwritten for deployment to any environment. +> As opposed to using the default values already defined in the `appsettings.json` files. -> This list is not inclusive of all other variables that you may want to change for your production environment, this list is the bare minimum that are REQUIRED to be replaced for deployment. If you add other secrets/variables to your GitHub project (using the same naming convention below), they will also be automatically applied to any `appsettings.json` files in the repository at deploy time too. +To this end, in the `appsettings.json` file, there is an additional section: `Deploy -> Required -> Keys` which declares the settings that MUST be overwritten for deployment to any environment. -To overwrite any settings in `appsettings.json` at deployment time, we must define an equivalent environment variable or secret in the GitHub project. +> This list is not inclusive of all other variables that you may want to change for your production environment, this list is the bare minimum that you are mandating that must be are REQUIRED to be replaced for deployment. If you add other secrets/variables to your GitHub project (using the same naming convention below), they will also be automatically applied to any `appsettings.json` files in the repository in the deploy pipeline also. -The naming convention is to take the fully qualified name in `appsettings.json`, and convert it to uppercase, and replace the following characters: `:`, `-` and `.` with `_`. +To be able to overwrite any settings in `appsettings.json` at deployment time, we must define an equivalent environment variable or secret in the GitHub project, using a specific naming convention. + +The naming convention is to take the fully qualified name in `appsettings.json`, and convert it to uppercase, and replace all but the following characters, with an underscore (`_` character. + +* Converted characters: anything but uppercase or lowercase alpha-numeric characters (i.e. `A-Z` or `a-z` or `_`). For example, if you had this setting in `appsettings.json`: ```json @@ -66,66 +70,112 @@ For example, if you had this setting in `appsettings.json`: "ApplicationServices": { "Persistence": { "Kurrent": { - "ConnectionString": "esdb://localhost:2113?tls=false" + "Connection-String1": "esdb://localhost:2113?tls=false" } } } } ``` -The equivalent GitHub variable/secret name of the setting above, would need to be: `APPLICATIONSERVICES_PERSISTENCE_KURRENT_CONNECTIONSTRING`. +The equivalent GitHub variable/secret name of the setting above, to substitute, would need to be defined: `APPLICATIONSERVICES_PERSISTENCE_KURRENT_CONNECTION_STRING1`. + +### Defining required settings -It is more than likely that at some point in time, during normal development a new setting will be added to the `appsettings.json` file, and the developer may also remember to mark it up as "required" in the `Deploy -> Required -> Keys` section of `appsettings.json`, but the developer may forget to add the equivalent variable/secret ot the GitHib project. In this case, the deployment will detect this problem, and fail the deployment, and the error message will tell you which setting is missing, and how to fix it. +As an engineer adds new settings to their `appsettings.json` files, they will be expected to determine if that setting either has a default value that is appropriate to "local" development AND "production", or whether this setting "requires" being substituted, and "requires" a unique value in a "production" environment. -This is one of the safeguards of using the `VariableSubstitution` custom action. +If they decide that this setting is "required" to be replaced in a specific "production" environment, then they can 'mark it up' in the following way, using a `Deploy -> Required -> Keys` section. + +```json +{ + "ApplicationServices": { + "Persistence": { + "Kurrent": { + "Connection-String1": "esdb://localhost:2113?tls=false" + } + } + }, + "Deploy": { + "Notes": "Lists the required configuration keys that must be overwritten (by the GitHub configuration action) when we deploy this host", + "Required": [ + { + "Description": "General settings from this appsettings.json", + "Keys": [ + "ApplicationServices:Persistence:Kurrent:Connection-String1", + ] + } + ] + } +} +``` + +Then, what should happen (in some out-of-band process) is that the engineer makes arrangements to define the equivalent secret or variable in the GitHub project. In the example above, its clearly a secret, and they would define the variable in a secret in the GitHib project, as `APPLICATIONSERVICES_PERSISTENCE_KURRENT_CONNECTION_STRING1`. + +However, it is possible that the engineer, either forgets this step, or they get distracted/blocked in harvesting the actual secret value, and the task is never completed. + +> Clearly, if the code that required this setting went to production, you would expect something in production to fall over, because the replacement setting was never defined. This produces an avoidable production issue. + +By using the custom GitHub Action `VariableSubstitution` (defined in the repository), the deploy pipeline will detect the missing GitHub variable/secret, and fail the deployment pipeline, with clear error identifying the missing "required" GitHub variable/secret. + +This is one of the safeguards provided by the `VariableSubstitution` custom action. ### GitHub environment variables Most of the required variables defined in the `Deploy -> Required -> Keys` section of `appsettings.json` should be self-explanatory. -Here are ones that might need further explanation, about their origin and use: +Here are ones that might need further explanation, about their origin and use, and in some cases how to generate them. + +**NOTE:** You MUST define these variables in a specific "production" environment for deployment. As GitHub does not support variables at the Organization or repository levels (as is the case for secrets). #### Operator whitelist Setting name: `HOSTS_ENDUSERSAPI_AUTHORIZATION_OPERATORWHITELIST` This is a semicolon `;` delimited list of email addresses of user accounts that are authorized to act as operators in the system. -This list is populated before the user accounts are registered and when they are registered later, they are promoted to `operator` status. -**IMPORTANT**: You should always have at least one operator account email address defined in this list before your software goes to production for the first time, otherwise you will have no other way to promote other operators in the system. +This list is populated before the respective user accounts are registered. When they are registered (later), that user will be automatically promoted to the `PlatformRoles.Operations` role. + +**IMPORTANT**: You should always have at least one operator account email address defined in this list before your software goes to production for the first time, otherwise you will have no other way to promote other operators in the system later. > For user accounts that already exist, they can be promoted by existing operator accounts. ### GitHub secrets -The following MUST be defined as secrets in yor GitHub project, NOT as environment variables: +The following MUST be defined as secrets in your GitHub project, NOT as environment variables: -> We recommend these are defined in the secrets of the environment, however, they can also be defined at the repository level, or at the organization level. +> We recommend that these are defined in the secrets of a specific "production" environment. However, they can also be defined at the repository level, or at the organization level. -**IMPORTANT**: You MUST generate new random secrets for your deployed services! -**IMPORTANT**: You MUST never re-use the secrets defined in this repository in your production environment. They are far too well known to anyone who has access to this repository, using them compromises your production environment. +**IMPORTANT**: You MUST generate new crypto related secrets for your deployed services! +**IMPORTANT**: You MUST never re-use the crypto related secrets defined in this repository in your specific "production" environment. They are far too well known to anyone who has access to this public repository. Using them compromises the security measures in place for your "production" deployment, and is an easy attack vector. #### HMAC signing key -For secrets such as `HOSTS_APIHOST1_HMACAUTHNSECRET` and `HOSTS_ANCILLARYAPI_HMACAUTHNSECRET`: +This is a symmetrical crypto secret used to secure communications between various HTTP clients to the `ApiHost1` host. + +**IMPORTANT:** This crypto secret must be the same for all clients talking to the same host (the host is the master record for it). + +> You CAN reuse this secret for communications to other hosts too. However, its recommended to have a different secrets where possible, in case one of the clients or host is compromised. + +For both secrets such as `HOSTS_APIHOST1_HMACAUTHNSECRET` and `HOSTS_ANCILLARYAPI_HMACAUTHNSECRET`: * Generate a random value using the [HMACSigner.GenerateKey()](https://github.com/jezzsantos/saastack/blob/main/src/Infrastructure.Web.Api.Common/HMACSigner.cs) method. > Note: You can run the unit tests for this class and copy the value of the generated key in the first test. -**IMPORTANT**: It is vital that all the HMAC signing keys on each deployed host are identical. You can have different values on different hosts, as long as all their client hosts are also updated. - #### CSRF secrets -For the CSRF `HOSTS_WEBSITEHOST_CSRFHMACSECRET` secret -* Generate a new random value using the [CSRFToken.GenerateKey()](https://github.com/jezzsantos/saastack/blob/main/src/Infrastructure.Web.Api.Common/HMACSigner.cs) method as above for HMAC secrets. +These crypto secrets are used to secure communications to the `WebsiteHost` BEEFE. The HMAC crypto secret is a symmetrical crypto key used to sign the CSRF cookie used between the JSApp and the `WebsiteHost` BEFFE. The AES crypto secret is a symmetrical crypto key used to encrypt the CSRF token used in the CSRF cookie. -> Note: You will want to use a different value than the HMAC signing keys. +For the CSRF `HOSTS_WEBSITEHOST_CSRFHMACSECRET` crypto secret +* Generate a new random value using the [HMACSigner.GenerateKey()](https://github.com/jezzsantos/saastack/blob/main/src/Infrastructure.Web.Api.Common/HMACSigner.cs) method, as above for HMAC secrets. -For the CSRF `HOSTS_WEBSITEHOST_CSRFAESSECRET` secret: +> Note: You will want to use a different value than the HMAC signing keys above, never the same value. + +For the CSRF `HOSTS_WEBSITEHOST_CSRFAESSECRET` crypto secret: * Generate a new random value using the [AesEncryptionService.GenerateAesSecret()](https://github.com/jezzsantos/saastack/blob/main/src/Infrastructure.Common/DomainServices/AesEncryptionService.cs) method. > Note: You can run the unit tests for this class and copy the value of the generated key in the first test. -#### SSO +#### Single Sign-On + +This is a symmetrical crypto secret used to encrypt the values of persisted SSO provider tokens that are stored at rest in the `IDataStore` (for example, the auth tokens from providers like Microsoft, Google, etc). For the `APPLICATIONSERVICES_SSOPROVIDERSSERVICE_SSOUSERTOKENS_AESSECRET` secret: * Generate a new random value using the [AesEncryptionService.GenerateAesSecret()](https://github.com/jezzsantos/saastack/blob/main/src/Infrastructure.Common/DomainServices/AesEncryptionService.cs) method. @@ -134,6 +184,8 @@ For the `APPLICATIONSERVICES_SSOPROVIDERSSERVICE_SSOUSERTOKENS_AESSECRET` secret #### JWT signing key +This is a symmetrical crypto secret used to sign the JWT authorization tokens (produced by this API) and passed between all API hosts and all HTTP clients. + For the `HOSTS_IDENTITYAPI_JWT_SIGNINGSECRET` secret: * Generate a new random value using the [JwtTokenService.GenerateSigningKey()](https://github.com/jezzsantos/saastack/blob/main/src/IdentityInfrastructure/ApplicationServices/JWTTokensService.cs) method. @@ -142,9 +194,12 @@ For the `HOSTS_IDENTITYAPI_JWT_SIGNINGSECRET` secret: ### Additional GitHub secrets and variables In order to deploy your code to Cloud based infrastructure (such as Azure or AWS) you will be using standard GitHub Actions to perform the deployment. + Most of these actions will require additional configuration with secrets to access your cloud provider and cloud accounts. -For example, when using the `deploy-azure.yml` file, you will need to define login secrets in order to deploy your code to Azure Infrastructure. +#### Azure deployments + +When using the `deploy-azure.yml` file, you will need to define login secrets in order to deploy your code to Azure Infrastructure. You can read about this process and the credentials required to do it in the [Azure Login Action](https://learn.microsoft.com/en-us/azure/app-service/deploy-github-actions?tabs=openid%2Caspnetcore). For deploying to Azure, (using the `deploy-azure.yml` deployment workflow) you will also need to define the following secrets and variables in your GitHub deployment environment: @@ -153,29 +208,37 @@ For deploying to Azure, (using the `deploy-azure.yml` deployment workflow) you w > These are used to automate the deployment to your Azure subscription. then, you will need these to define the physical components to deploy your code to: -* `DEPLOY_APIHOST1_APP_NAME` -* `DEPLOY_WEBSITEHOST_APP_NAME` -* `DEPLOY_AZUREFUNCTIONS_APP_NAME` +* `DEPLOY_AZURE_APIHOST1_APP_NAME` +* `DEPLOY_AZURE_WEBSITEHOST_APP_NAME` +* `DEPLOY_AZURE_AZUREFUNCTIONS_APP_NAME` + +> Note: We use the prefix `DEPLOY_` to group these settings as distinct from others in the GitHub project. -> Note: the prefix `DEPLOY_` groups these settings as distinct from others in the GitHub project. +#### AWS deployments + +TBD ## Triggering a deployment -The deployment script (`deploy-azure.yml` and `deploy-aws.yml`) are both triggered manually (by a human) on the `main` branch +The deployment script (`deploy-azure.yml` and `deploy-aws.yml`) are both triggered manually (by a human), on the `main` branch > See the GitHub event [workflow_dispatch](https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#workflow_dispatch). -> This is a safety feature to prevent accidental deployments in the course of normal development of your product. This step should be very intentional in environment where you are not practicing "Continuous Deployment". +This simple mechanism is a simple safety feature to prevent accidental deployments in the course of normal development of your product. + +> This step should be very intentional in any "production" environment where you are not practicing "Continuous Deployment". ## Initial Deployment -By default, we assume that before deployment of the software, you have already (manually/automated) the creation of the actual target infrastructure, in your preferred cloud environment. (i.e, in Azure, AWS or GC). +By default, we assume that before deployment of any software, you have already (manually/automated) the creation of the actual target infrastructure, in your preferred cloud environment. (i.e, in Azure, AWS or GC). + +When the automated deployment steps are run in the deployment pipeline, those steps are (by default) only deploying new software packages to existing running infrastructure components in the cloud. -When the automated deployment step is run, it is simply deploying new software packages to existing running infrastructure components. +> It is possible, to create a more automated deployment process where the infrastructure itself is re-provisioned every time a deployment occurs. You can do this with tools like Bicep and Terraform, etc. However, this automation is not discussed here. -> However, it is possible, to create a deployment process where the infrastructure itself is re-provisioned every time a deployment occurs. You can do this with tools like Terraform. However, this is not the default, and will not be discussed here. +### Azure infrastructure -### Build the initial infrastructure in Azure +This represents the main infrastructural pieces in an Azure Deployment. ![](images/Deployment-Azure.png) @@ -183,27 +246,34 @@ To build out an initial infrastructure in Azure, we can use an ARM template like > This template can be easily customized, and run once to create your initial infrastructure in Azure. > -> It has made some low cost choices to get started with. +> It has made some "low cost" choices to get started with, but lower cost is possible with some services, like SQLServer, ApplicationInsights and Storage accounts. -Open the template, and edit the values you see the `parameters` section, at the start of the file +1. Determine the name of your new resource group in Azure (e.g. `saastack`). + - This will be referenced in the commands below as `` -> These are essentially the names of the resource that will be created in your resource group in you Azure subscription, and you may want to change them to match your product +2. Edit the template, and review and change the values you see the `parameters` section at the start of the file -1. Determine the name of your new resource group in Azure. This will be referenced in the commands below as `` + > These are essentially the names of the resources that will be created in your resource group in you Azure subscription, and you may want to change them to match your product -2. To run this template, and deploy your new resource group, use the following commands: +3. Execute this template, use the following commands: -* `az login` and sign in to your Azure subscription. Your subscription ID will be listed. -* `az account set --subscription ` -* `az group create --name --location 'Central US'` make sure to -* `az deployment group create --name saastack-initial --resource-group --template-file '../iac/Azure/ARM/Azure-Seed.json'` + * `az login` and sign in to your Azure subscription. Your subscription ID will be listed. - * > You will be prompted for the admin username and password for the SQL server database (SQL authentication). + * `az account set --subscription ` + * `az group create --name --location 'Central US'` use the following command to list the regions `az account list-locations` (and use the `displayName` from this list) -### Deployment credentials + * `az deployment group create --name saastack-initial --resource-group --template-file '../iac/Azure/ARM/Azure-Seed.json'` -We now need to create a Service Principal to retrieve credentials, and the build pipeline will use these credentials to perform the automated deployment. + > You will be prompted for the admin username and password for the SQL server database (SQL authentication). + + + +#### Deployment credentials + +When the infrastructure is created, we now need to create a Service Principal to retrieve credentials, and the build pipeline will use these credentials to perform the automated deployment. + +Run this command: ```powershell az ad sp create-for-rbac --name "saastack" --role contributor --scopes /subscriptions//resourceGroups/ --json-auth @@ -213,7 +283,7 @@ az ad sp create-for-rbac --name "saastack" --role contributor --scopes /subscrip * where `` is the name of the resource group you created -> Warning: this service principal gives access to all the resources in the specific resource group +> **Warning**: this service principal actually gives access to all the resources in your specific resource group, rather than just specific resources. This command will return a response as a block of JSON, like this: @@ -232,17 +302,17 @@ This command will return a response as a block of JSON, like this: } ``` -Now, you will copy and paste that whole output text into a GitHub secret called: `DEPLOY_AZURE_CREDENTIALS`, either at the GitHub repository level, or as a secret into the GitHub deployment environment you are using. +Now, just copy and paste the whole output JSON (`{` to `}` like the above) into a GitHub secret called: `DEPLOY_AZURE_CREDENTIALS`, either at the GitHub repository level, or as a secret into the GitHub deployment environment you are using. -### Deployment variables and secrets +#### Deployment variables and secrets -Now that your Azure environment is provisioned, you need to update the following variables and secrets in your GitHub Project. +Now, that your Azure environment is provisioned, you need to update the following variables and secrets in your GitHub Project. Assign these GitHub variables (or secrets) in your deployment environment, depending on the technology adapters you are using : -* `APPLICATIONINSIGHTS_CONNECTIONSTRING` (read from Azure Portal: AppInsights -> Properties -> Connection String) -* `APPLICATIONSERVICES_PERSISTENCE_AZURESERVICEBUS_CONNECTIONSTRING` (read from Azure Portal: ServiceBus -> Shared Access Policies -> RootManageSharedAccessKey -> Primary Connection String) -* `APPLICATIONSERVICES_PERSISTENCE_AZURESTORAGEACCOUNT_ACCOUNTKEY` (read from Azure Portal: Storage Account -> Access keys -> key1) +* `APPLICATIONINSIGHTS_CONNECTIONSTRING` (read from Azure Portal: AppInsights -> Configure -> Properties -> Connection String) +* `APPLICATIONSERVICES_PERSISTENCE_AZURESERVICEBUS_CONNECTIONSTRING` (read from Azure Portal: ServiceBus -> Settings -> Shared Access Policies -> RootManageSharedAccessKey -> Primary Connection String) +* `APPLICATIONSERVICES_PERSISTENCE_AZURESTORAGEACCOUNT_ACCOUNTKEY` (read from Azure Portal: Storage Account -> Security + networking -> Access keys -> key1 -> Key) * `APPLICATIONSERVICES_PERSISTENCE_SQLSERVER_DBCREDENTIALS` (as defined in initial setup, in format: `User Id=;Password=`) * `APPLICATIONSERVICES_PERSISTENCE_AZURESTORAGEACCOUNT_ACCOUNTNAME` (as defined in initial setup) * `APPLICATIONSERVICES_PERSISTENCE_SQLSERVER_DBSERVERNAME` (as defined in initial setup) @@ -257,22 +327,35 @@ Assign these GitHub variables (or secrets) in your deployment environment, depen * `HOSTS_IMAGESAPI_BASEURL` (as defined in initial setup, in format: `https://.azurewebsites.com`) * `HOSTS_WEBSITEHOST_BASEURL` (as defined in initial setup, in format: `https://.azurewebsites.com`) -### Initialize SQL Database +#### Initialize SQL Database + +Your database has been created, but it has no schema at this point. -Your database has been created, but it has no schema at this point. You will need to initialize the schema. +You will need to initialize the schema manually. -Using your favorite database tool, connect to the database in Azure. +Using your favorite database tool (e.g. Microsoft SQL Server Management Studio), connect to the database in Azure. > By default, your Azure SQL database is protected by a firewall. > > In order to connect your local machine to Azure, you will need to set a firewall rule in the Azure Portal to allow access from your IP address. > -> Go to Azure Portal, then for the SQL database, in the Overview -> Set server firewall, then in the firewall rules, select "Add your client IPv4 address", and hit Save +> Go to Azure Portal: SQL server -> Security -> Networking, then in the firewall rules, select "Add your client IPv4 address", and hit Save Once you have access to your database, you can write the database schema files located at: `../iac/Azure/SQLServer`. -For each of these files, (in no particular order) edit the file, change the name of the database on the first line, and execute the entire file. +For each of these files, (in no particular order): + +1. Open the file +2. Change the name of the database in the `USE` statement, on the first line +3. Execute the entire file. + +#### Deploy your build + +Now that your Azure cloud infrastructure is up and running, its time to trigger a build and deploy the code to your infrastructure. + +In [this pipeline](https://github.com/jezzsantos/saastack/actions/workflows/deploy-azure.yml), manually deploy the latest build from the `main` branch to your Azure subscription. + +### AWS infrastructure -### Deploy your build +TBD -Now that your cloud infrastructure is up and running, its time to trigger a build and deploy the code to your infrastructure. \ No newline at end of file diff --git a/iac/Azure/ARM/Azure-Seed.json b/iac/Azure/ARM/Azure-Seed.json index 826cc9d5..8218090f 100644 --- a/iac/Azure/ARM/Azure-Seed.json +++ b/iac/Azure/ARM/Azure-Seed.json @@ -30,6 +30,10 @@ "Standard_RAGZRS" ] }, + "netFrameworkVersion": { + "defaultValue": "v8.0", + "type": "string" + }, "sqlserver_name": { "defaultValue": "saastack-sqlserver", "type": "string" @@ -81,8 +85,8 @@ "type": "string" } }, - "variables": { - "insights_analytics_name":"[concat(parameters('insights_name'), '-analytics')]" + "variables": { + "insights_analytics_name": "[concat(parameters('insights_name'), '-analytics')]" }, "resources": [ { @@ -146,7 +150,8 @@ { "type": "Microsoft.Sql/servers/firewallRules", "apiVersion": "2024-05-01-preview", - "name": "Allow Azure", + "name": "[concat(parameters('sqlserver_name'), '/', 'AllowAzure')]", + "location": "[parameters('location')]", "dependsOn": [ "[resourceId('Microsoft.Sql/servers', parameters('sqlserver_name'))]" ], @@ -333,10 +338,16 @@ "value": "[reference(resourceId('Microsoft.Insights/components', parameters('insights_name')), '2015-05-01').ConnectionString]" } ], - "netFrameworkVersion": "v8.0", - "use32BitWorkerProcess": false + "metadata": [ + { + "name": "CURRENT_STACK", + "value": "dotnet" + } + ], + "netFrameworkVersion": "[parameters('netFrameworkVersion')]" }, - "httpsOnly": true + "httpsOnly": true, + "publicNetworkAccess": "Enabled" } }, { @@ -371,10 +382,16 @@ "value": "[reference(resourceId('Microsoft.Insights/components', parameters('insights_name')), '2015-05-01').ConnectionString]" } ], - "netFrameworkVersion": "v8.0", - "use32BitWorkerProcess": false + "metadata": [ + { + "name": "CURRENT_STACK", + "value": "dotnet" + } + ], + "netFrameworkVersion": "[parameters('netFrameworkVersion')]", }, - "httpsOnly": true + "httpsOnly": true, + "publicNetworkAccess": "Enabled" } }, {