gio
03/27/2021, 7:39 AMapi.dev.rt.parque.cloud already exists in stack arn:aws:cloudformation:us-east-1:834896107245:stack/dev-rt-test-api/47c3a270-8c5e-11eb-88a6-0ed5acf75591
The complete logs is inside this threadgio
03/27/2021, 7:39 AM07:36:55 | UPDATE_IN_PROGRESS | AWS::CloudFormation::Stack | dev-rt-test-api
07:37:31 | CREATE_IN_PROGRESS | AWS::ApiGatewayV2::DomainName | booksapiDomainNameC02658E8
07:37:32 | CREATE_IN_PROGRESS | AWS::ApiGatewayV2::Api | booksapiApiD9E96849
07:37:32 | CREATE_FAILED | AWS::ApiGatewayV2::DomainName | booksapiDomainNameC02658E8 - api.dev.rt.parque.cloud already exists in stack arn:aws:cloudformation:us-east-1:834896107245:stack/dev-rt-test-api/47c3a270-8c5e-11eb-88a6-0ed5acf75591
07:37:32 | CREATE_IN_PROGRESS | AWS::IAM::Role | booksapiLambdaDELETEuriServiceRole1C7BDA10
07:37:32 | CREATE_IN_PROGRESS | AWS::IAM::Role | booksapiLambdaPUTuriServiceRole5C758468
07:37:32 | UPDATE_IN_PROGRESS | AWS::Lambda::Function | usersapiLambdaPUTuri34BE95F6
07:37:32 | CREATE_IN_PROGRESS | AWS::IAM::Role | booksapiLambdaGETServiceRole55FF2D79
07:37:32 | UPDATE_IN_PROGRESS | AWS::Lambda::Function | usersapiLambdaDELETEuriE80EFA6D
07:37:32 | CREATE_IN_PROGRESS | AWS::Logs::LogGroup | booksapiLogGroup865BFA94
07:37:32 | CREATE_IN_PROGRESS | AWS::IAM::Role | booksapiLambdaGETuriServiceRole56D21611
07:37:32 | CREATE_FAILED | AWS::ApiGatewayV2::Api | booksapiApiD9E96849 - Resource creation cancelled
07:37:32 | CREATE_FAILED | AWS::IAM::Role | booksapiLambdaGETuriServiceRole56D21611 - Resource creation cancelled
07:37:32 | CREATE_FAILED | AWS::IAM::Role | booksapiLambdaPUTuriServiceRole5C758468 - Resource creation cancelled
07:37:32 | CREATE_FAILED | AWS::IAM::Role | booksapiLambdaDELETEuriServiceRole1C7BDA10 - Resource creation cancelled
07:37:32 | CREATE_FAILED | AWS::Logs::LogGroup | booksapiLogGroup865BFA94 - Resource creation cancelled
07:37:32 | CREATE_FAILED | AWS::IAM::Role | booksapiLambdaGETServiceRole55FF2D79 - Resource creation cancelled
07:37:33 | UPDATE_FAILED | AWS::Lambda::Function | usersapiLambdaPUTuri34BE95F6 - Resource update cancelled
07:37:33 | UPDATE_FAILED | AWS::Lambda::Function | usersapiLambdaDELETEuriE80EFA6D - Resource update cancelled
07:37:34 | UPDATE_ROLLBACK_IN_PROGRESS | AWS::CloudFormation::Stack | dev-rt-test-api - The following resource(s) failed to create: [booksapiLambdaGETuriServiceRole56D21611, booksapiLambdaPUTuriServiceRole5C758468, booksapiLambdaGETServiceRole55FF2D79, booksapiLambdaDELETEuriServiceRole1C7BDA10, booksapiApiD9E96849, booksapiLogGroup865BFA94, booksapiDomainNameC02658E8]. The following resource(s) failed to update: [usersapiLambdaPUTuri34BE95F6, usersapiLambdaDELETEuriE80EFA6D].
07:37:57 | UPDATE_IN_PROGRESS | AWS::Lambda::Function | usersapiLambdaPUTuri34BE95F6
07:37:57 | UPDATE_IN_PROGRESS | AWS::Lambda::Function | usersapiLambdaDELETEuriE80EFA6D
07:37:58 | UPDATE_COMPLETE | AWS::Lambda::Function | usersapiLambdaPUTuri34BE95F6
07:37:58 | UPDATE_COMPLETE | AWS::Lambda::Function | usersapiLambdaDELETEuriE80EFA6D
07:38:00 | UPDATE_ROLLBACK_COMPLETE_CLEANUP_IN_PROGRESS | AWS::CloudFormation::Stack | dev-rt-test-api
07:38:02 | DELETE_COMPLETE | AWS::IAM::Role | booksapiLambdaGETuriServiceRole56D21611
07:38:02 | DELETE_IN_PROGRESS | AWS::IAM::Role | booksapiLambdaDELETEuriServiceRole1C7BDA10
07:38:02 | DELETE_IN_PROGRESS | AWS::IAM::Role | booksapiLambdaPUTuriServiceRole5C758468
07:38:03 | DELETE_COMPLETE | AWS::ApiGatewayV2::DomainName | booksapiDomainNameC02658E8
07:38:03 | DELETE_COMPLETE | AWS::IAM::Role | booksapiLambdaGETServiceRole55FF2D79
07:38:03 | DELETE_COMPLETE | AWS::ApiGatewayV2::Api | booksapiApiD9E96849
07:38:04 | DELETE_COMPLETE | AWS::IAM::Role | booksapiLambdaPUTuriServiceRole5C758468
07:38:04 | DELETE_COMPLETE | AWS::IAM::Role | booksapiLambdaDELETEuriServiceRole1C7BDA10
Dmitry Pavluk
03/27/2021, 7:42 AMDmitry Pavluk
03/27/2021, 7:43 AMgio
03/27/2021, 7:44 AMDmitry Pavluk
03/27/2021, 7:45 AMgio
03/27/2021, 7:46 AMimport * as sst from "@serverless-stack/resources";
import {Certificate, CertificateValidation} from "@aws-cdk/aws-certificatemanager";
import {HostedZone} from '@aws-cdk/aws-route53';
import {CfnOutput} from "@aws-cdk/core";
import {ApiAuthorizationType} from "@serverless-stack/resources";
import * as cdk from "@aws-cdk/core";
function createCertificate(scope: sst.Stack, app: <http://sst.App|sst.App>, id: string, props?: sst.StackProps): Certificate {
const domain_name = `${app.stage}.rt.parque.cloud`;
const hosted_zone = HostedZone.fromLookup(scope, 'HostedZone', {
domainName: domain_name,
});
const certificate: Certificate = new Certificate(scope, "CustomDomainCertificate", {
domainName: `*.${domain_name}`,
validation: CertificateValidation.fromDns(hosted_zone),
});
new CfnOutput(scope, "CertificateArn", {
value: certificate.certificateArn,
});
return certificate;
}
function createBooksApi(stack: sst.Stack, app: <http://sst.App|sst.App>, id: string, props?: sst.StackProps): sst.Api {
const books_api: sst.Api = new sst.Api(stack, id, {
accessLog:
"$context.identity.sourceIp,$context.requestTime,$context.httpMethod,$context.routeKey,$context.protocol,$context.status,$context.responseLength,$context.requestId",
customDomain: {
domainName: "api.dev.rt.parque.cloud",
hostedZone: "dev.rt.parque.cloud",
path: "books",
certificate: Certificate.fromCertificateArn(
stack,
"books-api-certificate",
'arn:aws:acm:us-east-1:834896107245:certificate/d9f2ee08-fc7b-4b09-819e-d0289f904ac4'
),
},
defaultAuthorizationType: ApiAuthorizationType.AWS_IAM,
defaultFunctionProps: {
timeout: 30,
environment: {
STAGE: app.stage
}
},
routes: {
"POST /": "sample-app/books-api/books-api.createBook",
"GET /{uri}": "sample-app/books-api/books-api.readBook",
"PUT /{uri}": "sample-app/books-api/books-api.updateBook",
"DELETE /{uri}": "sample-app/books-api/books-api.deleteBook",
"GET /": "sample-app/books-api/books-api.findBook",
}
});
books_api.attachPermissions(["dynamodb"]);
return books_api;
}
function createAuth(stack: sst.Stack, app: <http://sst.App|sst.App>, id: string, props?: sst.StackProps): sst.Auth {
// Create auth provider
const auth = new sst.Auth(stack, id, {
// Create a Cognito User Pool to manage user's authentication info.
cognito: {
// Users will login using their email and password
signInAliases: {email: true},
},
});
new cdk.CfnOutput(stack, "UserPoolId", {
value: auth.cognitoUserPool!.userPoolId,
});
new cdk.CfnOutput(stack, "UserPoolClientId", {
value: auth.cognitoUserPoolClient!.userPoolClientId,
});
new cdk.CfnOutput(stack, "IdentityPoolId", {
value: auth.cognitoCfnIdentityPool.ref,
});
return auth;
}
function createUsersApi(stack: sst.Stack, app: <http://sst.App|sst.App>, id: string, props?: sst.StackProps): sst.Api {
const users_api: sst.Api = new sst.Api(stack, id, {
accessLog:
"$context.identity.sourceIp,$context.requestTime,$context.httpMethod,$context.routeKey,$context.protocol,$context.status,$context.responseLength,$context.requestId",
customDomain: {
domainName: "api.dev.rt.parque.cloud",
hostedZone: "dev.rt.parque.cloud",
path: "users",
certificate: Certificate.fromCertificateArn(
stack,
"users-api-certificate",
'arn:aws:acm:us-east-1:834896107245:certificate/d9f2ee08-fc7b-4b09-819e-d0289f904ac4'
),
},
defaultAuthorizationType: ApiAuthorizationType.AWS_IAM,
defaultFunctionProps: {
timeout: 30,
environment: {
STAGE: app.stage
}
},
routes: {
"POST /": "sample-app/users-api/users-api.createUser",
"GET /{uri}": "sample-app/users-api/users-api.readUser",
"PUT /{uri}": "sample-app/users-api/users-api.updateUser",
"DELETE /{uri}": "sample-app/users-api/users-api.deleteUser",
"GET /": "sample-app/users-api/users-api.findUser",
}
});
users_api.attachPermissions(["dynamodb"]);
return users_api;
}
export default class TestApiStack extends sst.Stack {
constructor(app: <http://sst.App|sst.App>, id: string, props?: sst.StackProps) {
super(app, id, props);
createCertificate(this, app, 'certificate');
const auth: sst.Auth = createAuth(this, app, 'users-auth');
const users_api: sst.Api = createUsersApi(this, app, 'users-api');
const books_api: sst.Api = createBooksApi(this, app, 'books-api');
auth.attachPermissionsForAuthUsers([users_api, books_api]);
}
}
gio
03/27/2021, 7:47 AMDmitry Pavluk
03/27/2021, 8:04 AMApi.ts
in sst/resources
and at CDK docs - didn't see any obvious reason this should fail. Maybe it's a dangling resource left behind from previous deploys? Have you tried manually removing that resource from the stack and/or removing the whole stack and redeploying?Dmitry Pavluk
03/27/2021, 8:07 AMsst start
and/or sst deploy
Dmitry Pavluk
03/27/2021, 8:08 AMDmitry Pavluk
03/27/2021, 8:12 AMgio
03/27/2021, 8:19 AMgio
03/27/2021, 8:20 AMDmitry Pavluk
03/27/2021, 8:23 AMgio
03/27/2021, 8:25 AMDmitry Pavluk
03/27/2021, 8:26 AMgio
03/27/2021, 8:47 AMcustomDomain: {
domainName: apigatewayv2.DomainName.fromDomainNameAttributes(
stack,
"books-api-domain",
{
name: 'api.dev.rt.parque.cloud',
regionalDomainName: 'api.dev.rt.parque.cloud',
regionalHostedZoneId: 'dev.rt.parque.cloud',
}
),
path: "books"
},
SOLVED importing an existing domain through CDK as described is SST docs.Frank
apigatewayv2.DomainName.fromDomainNameAttributes
might fail if CloudFormation configured the domain for books_api before users_api.Frank
// Create users api normally
const usersApi = new sst.Api(this, "UsersAPI", {
customDomain: ...what u had before...
});
// Create books api and re-use users api's domain
const booksApi = new sst.Api(this, "BooksAPI", {
customDomain: {
domainName: usersApi.apiGatewayDomain,
path: "books"
}
});
Added an example to the doc - https://docs.serverless-stack.com/constructs/Api#mapping-multiple-apis-to-the-same-domainFrank
gio
03/28/2021, 7:39 PMPål Brattberg
04/07/2021, 8:10 PMthis.apiV3 = new sst.Api(this, `${id}_apiV3`, {
customDomain: {
domainName: apigatewayv2.DomainName.fromDomainNameAttributes(
this,
'PeasyCustomDomain',
{
name: domainName,
regionalDomainName: domainName,
regionalHostedZoneId: '<http://peasy.nu|peasy.nu>'
}
),
certificate: certificatemanager.Certificate.fromCertificateArn(
this,
'PeasyCustomCertificate',
certArn[scope.stage]
)
},
....
But running this yields Error: Cannot configure the "certificate" when the "domainName" is a construct
Any ideas on how to do this smarter @Frank? I'm open to deleting the domain and/or certificate to create from scratch, but need different domains for my various environments and these are all in different accounts, so I'll need to add NS records for the new domains to the root domain (which is why I created my domains manually)Frank
Frank
Pål Brattberg
04/07/2021, 8:29 PMPål Brattberg
04/07/2021, 8:29 PMPål Brattberg
04/07/2021, 10:03 PMtest-peasy-domain | CREATE_FAILED | AWS::ApiGatewayV2::ApiMapping | domainapiV2p1ApiDefaultStagetestpeasydomainPeasyCustomDomainundefinedD2651C5C Invalid domain name identifier specified (Service: AmazonApiGatewayV2; Status Code: 404; Error Code: NotFoundException; Request ID: xxxx; Proxy: null)
If I only create the domain and just output a dump of the object, that runs fine:
this.domain = apigatewayv2.DomainName.fromDomainNameAttributes(
this, 'PeasyCustomDomain',
{
name: this.domainName,
regionalDomainName: this.domainName,
regionalHostedZoneId: '<http://api.peasy.nu|api.peasy.nu>'
}
)
Any ideas on what I'm doing wrong? I checked the dump and it's in the correct regionFrank
path
? (ie. basemap)Pål Brattberg
04/07/2021, 10:15 PMFrank
Frank
Pål Brattberg
04/07/2021, 10:17 PMPål Brattberg
04/07/2021, 10:19 PMFrank
.build/cdk.out
?Pål Brattberg
04/07/2021, 10:21 PMPål Brattberg
04/08/2021, 10:08 PM