From 0b564a6938286cddfd7bde6d343acacd0b558533 Mon Sep 17 00:00:00 2001 From: Kevin BEAUGRAND <9513635+kbeaugrand@users.noreply.github.com> Date: Wed, 17 May 2023 11:26:43 +0200 Subject: [PATCH] Fix AWS cloud formation template (#2087) --- src/AzureIoTHub.Portal.sln | 41 ++++++---- templates/aws/awsdeploy.yml | 144 ++++++++++++++++++++++++------------ 2 files changed, 123 insertions(+), 62 deletions(-) diff --git a/src/AzureIoTHub.Portal.sln b/src/AzureIoTHub.Portal.sln index 5f6bd7637..4c09012a5 100644 --- a/src/AzureIoTHub.Portal.sln +++ b/src/AzureIoTHub.Portal.sln @@ -26,22 +26,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{3CA1 EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "templates", "templates", "{3F7A2982-4F0B-45F9-9FCA-923D5A7A1511}" - ProjectSection(SolutionItems) = preProject - ..\templates\app_insights.bicep = ..\templates\app_insights.bicep - ..\templates\app_service.bicep = ..\templates\app_service.bicep - ..\templates\app_service_plan.bicep = ..\templates\app_service_plan.bicep - ..\templates\azuredeploy.bicep = ..\templates\azuredeploy.bicep - ..\templates\azuredeployUI.json = ..\templates\azuredeployUI.json - ..\templates\blob_container.bicep = ..\templates\blob_container.bicep - ..\templates\database.bicep = ..\templates\database.bicep - ..\templates\dps.bicep = ..\templates\dps.bicep - ..\templates\iothub.bicep = ..\templates\iothub.bicep - ..\templates\iothub_eventhub_consumer_group.bicep = ..\templates\iothub_eventhub_consumer_group.bicep - ..\templates\portal_without_lorawan.bicep = ..\templates\portal_without_lorawan.bicep - ..\templates\portal_with_lorawan.bicep = ..\templates\portal_with_lorawan.bicep - ..\templates\portal_with_lorawan_and_starter_kit.bicep = ..\templates\portal_with_lorawan_and_starter_kit.bicep - ..\templates\storage.bicep = ..\templates\storage.bicep - EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ISSUE_TEMPLATES", "ISSUE_TEMPLATES", "{4B89F192-EA33-4121-9809-D99621C40F3B}" ProjectSection(SolutionItems) = preProject @@ -75,6 +59,29 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AzureIoTHub.Portal.Postgres EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AzureIoTHub.Portal.Tests.E2E", "AzureIoTHub.Portal.Tests.E2E\AzureIoTHub.Portal.Tests.E2E.csproj", "{9C5F753D-B9A3-4E7F-BFF3-37EB8BEBAB80}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "aws", "aws", "{AD6BE5D2-0FFB-4629-8DE1-67D9B4D39577}" + ProjectSection(SolutionItems) = preProject + ..\templates\aws\awsdeploy.yml = ..\templates\aws\awsdeploy.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "azure", "azure", "{EB8B706B-11C0-4D53-B5DA-603A4C1668F9}" + ProjectSection(SolutionItems) = preProject + ..\templates\app_insights.bicep = ..\templates\app_insights.bicep + ..\templates\app_service.bicep = ..\templates\app_service.bicep + ..\templates\app_service_plan.bicep = ..\templates\app_service_plan.bicep + ..\templates\azuredeploy.bicep = ..\templates\azuredeploy.bicep + ..\templates\azuredeployUI.json = ..\templates\azuredeployUI.json + ..\templates\blob_container.bicep = ..\templates\blob_container.bicep + ..\templates\database.bicep = ..\templates\database.bicep + ..\templates\dps.bicep = ..\templates\dps.bicep + ..\templates\iothub.bicep = ..\templates\iothub.bicep + ..\templates\iothub_eventhub_consumer_group.bicep = ..\templates\iothub_eventhub_consumer_group.bicep + ..\templates\portal_without_lorawan.bicep = ..\templates\portal_without_lorawan.bicep + ..\templates\portal_with_lorawan.bicep = ..\templates\portal_with_lorawan.bicep + ..\templates\portal_with_lorawan_and_starter_kit.bicep = ..\templates\portal_with_lorawan_and_starter_kit.bicep + ..\templates\storage.bicep = ..\templates\storage.bicep + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -137,6 +144,8 @@ Global {3CA153F4-1172-4AF5-B483-94200717AB5C} = {20EC2171-363D-4DDE-827F-F56D4FE9ADF3} {3F7A2982-4F0B-45F9-9FCA-923D5A7A1511} = {20EC2171-363D-4DDE-827F-F56D4FE9ADF3} {4B89F192-EA33-4121-9809-D99621C40F3B} = {3CA153F4-1172-4AF5-B483-94200717AB5C} + {AD6BE5D2-0FFB-4629-8DE1-67D9B4D39577} = {3F7A2982-4F0B-45F9-9FCA-923D5A7A1511} + {EB8B706B-11C0-4D53-B5DA-603A4C1668F9} = {3F7A2982-4F0B-45F9-9FCA-923D5A7A1511} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {E03F3EEB-7B2B-4849-A581-4150BF29EC2B} diff --git a/templates/aws/awsdeploy.yml b/templates/aws/awsdeploy.yml index 9d1a9bd5c..ae338c2ed 100644 --- a/templates/aws/awsdeploy.yml +++ b/templates/aws/awsdeploy.yml @@ -1,5 +1,33 @@ AWSTemplateFormatVersion: 2010-09-09 -Description: AWS S3 storage template | PostgreSQL database storage template | Application Runner template +Description: IoT Hub portal deployment template + +Metadata: + AWS::CloudFormation::Interface: + ParameterGroups: + - Label: + default: "Solution" + Parameters: + - UniqueSolutionPrefix + - Label: + default: "Amazon Web Services resources access" + Parameters: + - awsAccess + - awsAccessSecretkey + - Label: + default: "PostgreSQL" + Parameters: + - pgsqlAdminLogin + - pgsqlAdminPassword + - Label: + default: "Open ID" + Parameters: + - openIdApiClientId + - openIdClientId + - openIdAuthority + - openIdMetadataURL + - openIdScopeName + ParameterLabels: + ParameterLabel Parameters: UniqueSolutionPrefix: @@ -10,22 +38,24 @@ Parameters: ConstraintDescription: Should be less than 20 letters AllowedPattern: '^[a-z]+$' pgsqlAdminLogin: - Type: String - Description: PostgreSQL user - MinLength: '1' - MaxLength: '30' + Type: String + Description: PostgreSQL user + MinLength: '1' + MaxLength: '30' pgsqlAdminPassword: - Type: String - NoEcho: 'true' - Description: PostgreSQL password - MinLength: '8' - MaxLength: '41' + Type: String + NoEcho: 'true' + Description: PostgreSQL password + MinLength: '8' + MaxLength: '41' awsAccess: Type: String - Description: AWS Access Sectet + Description: AWS Access Secret + NoEcho: 'true' awsAccessSecretkey: Type: String Description: AWS Access Secret Key + NoEcho: 'true' openIdApiClientId: Type: String Description: The Open ID API client ID for the B2C tenant @@ -46,15 +76,6 @@ Parameters: Type: String Description: The Open ID Scope name Default: iot_access - VPC: - Type: AWS::EC2::VPC::Id - Subnet1: - Type: AWS::EC2::Subnet::Id - Subnet2: - Type: AWS::EC2::Subnet::Id - Subnet3: - Type: AWS::EC2::Subnet::Id - Resources: #======== S3 Storage ========== @@ -71,15 +92,35 @@ Resources: Rules: - ObjectOwnership: BucketOwnerPreferred AccessControl: AwsExecRead +#======== Virtual Private Cloud ========== + VPC: + Type: AWS::EC2::VPC + Properties: + CidrBlock: 10.0.0.0/16 + EnableDnsSupport: 'true' + EnableDnsHostnames: 'true' + Subnet1: + Type: AWS::EC2::Subnet + Properties: + VpcId: + Ref: VPC + CidrBlock: 10.0.0.0/27 + AvailabilityZone: !Join [ "", [!Ref AWS::Region, "a"] ] + Subnet2: + Type: AWS::EC2::Subnet + Properties: + VpcId: + Ref: VPC + CidrBlock: 10.0.0.32/27 + AvailabilityZone: !Join [ "", [!Ref AWS::Region, "b"] ] + #======== PostgreSQL database ========== PostgreSQLDB: Type: AWS::RDS::DBInstance - DeletionPolicy: Retain # CloudFormation keeps the resource without deleting the resource or its contents when the resource is replaced - UpdateReplacePolicy: Retain # see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-updatereplacepolicy.html Properties: - DBInstanceIdentifier: !Join [ "-", [!Ref UniqueSolutionPrefix, "pgdbinstance"] ] + DBInstanceIdentifier: !Join [ "-", [!Ref UniqueSolutionPrefix, "pgdb"] ] AllocatedStorage: '20' - DBInstanceClass: 'db.t2.micro' # A voir quel instance utilisé : https://instances.vantage.sh/rds/ + DBInstanceClass: 'db.t2.micro' Engine: postgres EngineVersion: 12 LicenseModel: postgresql-license @@ -89,6 +130,7 @@ Resources: VPCSecurityGroups: - !Ref PgSQLSecurityGroup DBSubnetGroupName: !Ref PgSQLSubnetGroup + PgSQLSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: @@ -99,21 +141,21 @@ Resources: FromPort: 5432 ToPort: 5432 CidrIp: 0.0.0.0/0 + PgSQLSubnetGroup: Type: AWS::RDS::DBSubnetGroup Properties: DBSubnetGroupName: !Join [ "-", [!Ref UniqueSolutionPrefix, "pgSqlsubnetgroup"] ] DBSubnetGroupDescription: Subnets available for the database - SubnetIds: # A voir le nombre de subnets qu'on veut dans le groupe + SubnetIds: - !Ref Subnet1 - !Ref Subnet2 - - !Ref Subnet3 - + #InstanceRoleArn InstanceRole: Type: AWS::IAM::Role Properties: - RoleName: !Join [ "-", [!Ref UniqueSolutionPrefix, "AWSAppRunner"] ] + RoleName: !Join [ "-", [!Ref UniqueSolutionPrefix, "AppRunner"] ] AssumeRolePolicyDocument: Version: '2012-10-17' Statement: @@ -191,35 +233,27 @@ Resources: # AWS Secrets Manager SMAWSKey: Type: AWS::SecretsManager::Secret - DeletionPolicy: Retain # CloudFormation keeps the resource without deleting the resource or its contents when the resource is replaced - UpdateReplacePolicy: Retain # see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-updatereplacepolicy.html Properties: - Name: !Join [ "-", [!Ref UniqueSolutionPrefix, "SMAWSKey"] ] + Name: !Join [ "-", [!Ref UniqueSolutionPrefix, "AWSKey"] ] SecretString: !Sub '${awsAccess}' SMAWSSecretKey: Type: AWS::SecretsManager::Secret - DeletionPolicy: Retain # CloudFormation keeps the resource without deleting the resource or its contents when the resource is replaced - UpdateReplacePolicy: Retain # see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-updatereplacepolicy.html Properties: - Name: !Join [ "-", [!Ref UniqueSolutionPrefix, "SMAWSSecretKey"] ] + Name: !Join [ "-", [!Ref UniqueSolutionPrefix, "AWSSecretKey"] ] SecretString: !Sub '${awsAccessSecretkey}' - SMPostgreConnection: + SMPostgreSQLConnectionString: Type: AWS::SecretsManager::Secret - DeletionPolicy: Retain # CloudFormation keeps the resource without deleting the resource or its contents when the resource is replaced - UpdateReplacePolicy: Retain # see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-updatereplacepolicy.html Properties: - Name: !Join [ "-", [!Ref UniqueSolutionPrefix, "SMPostgreConnection"] ] + Name: !Join [ "-", [!Ref UniqueSolutionPrefix, "PostgreSQLConnectionString"] ] SecretString: !Join - "" - - 'Server=' - !GetAtt PostgreSQLDB.Endpoint.Address - !Sub ';Database=${UniqueSolutionPrefix}_cgigeiotdemo;Port=5432;User Id=${pgsqlAdminLogin};Password=${pgsqlAdminPassword};Pooling=true;Connection Lifetime=0;Command Timeout=0;' - SMAWSStorageConnection: + SMS3StorageConnectionString: Type: AWS::SecretsManager::Secret - DeletionPolicy: Retain # CloudFormation keeps the resource without deleting the resource or its contents when the resource is replaced - UpdateReplacePolicy: Retain # see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-updatereplacepolicy.html Properties: - Name: !Join [ "-", [!Ref UniqueSolutionPrefix, "SMAWSStorageConnection"] ] + Name: !Join [ "-", [!Ref UniqueSolutionPrefix, "S3StorageConnectionString"] ] SecretString: !Join - "" - - !Sub 'https://s3.${AWS::Region}.amazonaws.com/' @@ -227,11 +261,11 @@ Resources: - '-bucket' -#============= App Runner - To Lunch the App ============== +#============= App Runner - To Launch the App ============== AppRunnerService: Type: AWS::AppRunner::Service Properties: - ServiceName: !Join [ "-", [!Ref UniqueSolutionPrefix, "appRunner"] ] + ServiceName: !Join [ "-", [!Ref UniqueSolutionPrefix, "portal"] ] InstanceConfiguration: Cpu: 1 vCPU Memory: 2 GB @@ -243,6 +277,10 @@ Resources: Timeout: 5 HealthyThreshold: 2 UnhealthyThreshold: 5 + NetworkConfiguration: + EgressConfiguration: + EgressType: VPC + VpcConnectorArn: !Ref AppRunnerServiceVPCConnector SourceConfiguration: AutoDeploymentsEnabled: false AuthenticationConfiguration: @@ -256,10 +294,14 @@ Resources: - Name: AWS__AccessSecret Value: !Ref SMAWSSecretKey - Name: PostgreSQL__ConnectionString - Value: !Ref SMPostgreConnection + Value: !Ref SMPostgreSQLConnectionString - Name: AWS__S3Storage__ConnectionString - Value: !Ref SMAWSStorageConnection + Value: !Ref SMS3StorageConnectionString RuntimeEnvironmentVariables: + - Name: AWS__Region + Value: !Ref AWS::Region + - Name: AWS__BucketName + Value: !Join [ "-", [!Ref UniqueSolutionPrefix, "bucket"] ] - Name: OIDC__ApiClientId Value: !Sub '${openIdApiClientId}' - Name: OIDC__ClientId @@ -276,6 +318,16 @@ Resources: Value: IoT Portal DEMO - AWS ImageIdentifier: !Sub '${AWS::AccountId}.dkr.ecr.${AWS::Region}.amazonaws.com/iot-hub-portal:latest' ImageRepositoryType: ECR + + AppRunnerServiceVPCConnector: + Type: AWS::AppRunner::VpcConnector + Properties: + VpcConnectorName: !Join [ "-", [!Ref UniqueSolutionPrefix, "portal-vpc-connector"] ] + Subnets: + - !Ref Subnet1 + - !Ref Subnet2 + SecurityGroups: + - !Ref PgSQLSecurityGroup #GreenGras role script GreenGrasRole: