Related
I am trying to include security header information in my Swagger specifications and SwaggerUI, as I would like 3rd party systems to easily consume my API, as well as to be able to use CodeGen tools like Swagger CodeGen and NSwag to generate client libraries from it.
I am using Swashbuckle / SwaggerGen to generate the documentation automatically during runtime.
My API makes use of bearer token authentication, and I want my SwaggerDocs to reflect my authentication scheme. I would also be able to see the required security header information in my SwaggerDocs, and have the ability for clients to test out authentication via SwaggerUI when trying out API calls.
However, I have been unable to successfully have my security header information appear on my own solution.
This is what my I currently see when I view my SwaggerDocs through my local SwaggerUI during debugging:
Note that the generated OAS3 markup is correctly rendered when running it through https://editor.swagger.io/
Here is the actual markup that gets produced by Swashbuckle / SwaggerGen:
{
"openapi": "3.0.1",
"info": {
"title": "MyDemo Host API v1",
"version": "v1"
},
"paths": {
"/api/services/app/Tenant/CreateTenant": {
"post": {
"tags": [
"Tenant"
],
"operationId": "ApiServicesAppTenantCreatetenantPost",
"requestBody": {
"content": {
"application/json-patch+json": {
"schema": {
"allOf": [
{
"$ref": "#/components/schemas/CreateTenantInput"
}
]
}
},
"application/json": {
"schema": {
"allOf": [
{
"$ref": "#/components/schemas/CreateTenantInput"
}
]
}
},
"text/json": {
"schema": {
"allOf": [
{
"$ref": "#/components/schemas/CreateTenantInput"
}
]
}
},
"application/*+json": {
"schema": {
"allOf": [
{
"$ref": "#/components/schemas/CreateTenantInput"
}
]
}
}
}
},
"responses": {
"200": {
"description": "Success",
"content": {
"text/plain": {
"schema": {
"$ref": "#/components/schemas/SwaggerDocResponseWrapper"
}
},
"application/json": {
"schema": {
"$ref": "#/components/schemas/SwaggerDocResponseWrapper"
}
},
"text/json": {
"schema": {
"$ref": "#/components/schemas/SwaggerDocResponseWrapper"
}
}
}
}
},
"security": [
{
"bearer": []
}
]
}
}
},
"components": {
"schemas": {
"CreateTenantInput": {
"required": [
"adminEmailAddress",
"name",
"tenancyName"
],
"type": "object",
"properties": {
"tenancyName": {
"maxLength": 64,
"minLength": 0,
"pattern": "^[a-zA-Z][a-zA-Z0-9_-]{1,}$",
"type": "string"
},
"name": {
"maxLength": 128,
"minLength": 0,
"type": "string"
},
"adminEmailAddress": {
"maxLength": 256,
"minLength": 0,
"type": "string",
"format": "email"
},
"adminPassword": {
"maxLength": 128,
"minLength": 0,
"type": "string",
"nullable": true
},
"connectionString": {
"maxLength": 1024,
"type": "string",
"nullable": true
},
"shouldChangePasswordOnNextLogin": {
"type": "boolean"
},
"sendActivationEmail": {
"type": "boolean"
},
"editionId": {
"type": "integer",
"format": "int32",
"nullable": true
},
"isActive": {
"type": "boolean"
},
"subscriptionEndDateUtc": {
"type": "string",
"format": "date-time",
"nullable": true
},
"isInTrialPeriod": {
"type": "boolean"
},
"onSellingPartnerId": {
"type": "integer",
"format": "int32",
"nullable": true
},
"onSellingPartner": {
"allOf": [
{
"$ref": "#/components/schemas/OnSellingPartnerDto"
}
],
"nullable": true
},
"contactPersonFirstName": {
"maxLength": 32,
"type": "string",
"nullable": true
},
"contactPersonLastName": {
"maxLength": 32,
"type": "string",
"nullable": true
},
"contactNumber": {
"maxLength": 24,
"type": "string",
"nullable": true
},
"contactEmail": {
"maxLength": 256,
"type": "string",
"nullable": true
},
"taxNumber": {
"maxLength": 24,
"type": "string",
"nullable": true
},
"registeredName": {
"maxLength": 256,
"type": "string",
"nullable": true
},
"tenantBillingAddress": {
"allOf": [
{
"$ref": "#/components/schemas/TenantBillingAddressInput"
}
],
"nullable": true
}
},
"additionalProperties": false
},
"ValidationError": {
"type": "object",
"properties": {
"message": {
"type": "string",
"nullable": true
},
"members": {
"type": "array",
"items": {
"type": "string"
},
"nullable": true
}
},
"additionalProperties": false
},
"SwaggerDocResponseWrapper": {
"type": "object",
"properties": {
"result": {
"type": "string",
"nullable": true
},
"targetUrl": {
"type": "string",
"nullable": true
},
"success": {
"type": "boolean"
},
"error": {
"allOf": [
{
"$ref": "#/components/schemas/ResponseError"
}
],
"nullable": true
},
"unauthorizedRequest": {
"type": "boolean"
},
"__Abp": {
"type": "boolean"
}
},
"additionalProperties": false
},
"OnSellingPartnerDto": {
"type": "object",
"properties": {
"name": {
"type": "string",
"nullable": true
},
"isActive": {
"type": "boolean"
},
"registeredName": {
"type": "string",
"nullable": true
},
"taxNumber": {
"type": "string",
"nullable": true
},
"contactNumber": {
"type": "string",
"nullable": true
},
"contactPersonFirstName": {
"type": "string",
"nullable": true
},
"contactPersonLastName": {
"type": "string",
"nullable": true
},
"contactEmail": {
"type": "string",
"nullable": true
},
"id": {
"type": "integer",
"format": "int32"
}
},
"additionalProperties": false
},
"TenantBillingAddressInput": {
"type": "object",
"properties": {
"streetAddress": {
"maxLength": 256,
"minLength": 0,
"type": "string",
"nullable": true
},
"region": {
"maxLength": 64,
"minLength": 0,
"type": "string",
"nullable": true
},
"city": {
"maxLength": 64,
"minLength": 0,
"type": "string",
"nullable": true
},
"countryId": {
"type": "integer",
"format": "int32",
"nullable": true
},
"regionCode": {
"maxLength": 6,
"minLength": 0,
"type": "string",
"nullable": true
}
},
"additionalProperties": false
},
"ResponseError": {
"type": "object",
"properties": {
"code": {
"type": "integer",
"format": "int32"
},
"message": {
"type": "string",
"nullable": true
},
"details": {
"type": "string",
"nullable": true
},
"validationErrors": {
"type": "array",
"items": {
"$ref": "#/components/schemas/ValidationError"
},
"nullable": true
}
},
"additionalProperties": false
}
},
"securitySchemes": {
"bearer": {
"type": "http",
"description": "Specify the authorization token.",
"scheme": "bearer",
"bearerFormat": "JWT"
}
}
},
"security": [
{}
]
}
Environment:
AspNetZero Core MVC & JQuery v10.3.0 (.Net 5)
Abp (6.2.0)
Swashbuckle.AspNetCore (5.6.3) & Swashbuckle.AspNetCore.NewtonSoft (6.1.4)
The following code gets called from the ConfiguredServices method in Startup.cs:
public override void InstallServices(IHostEnvironment hostEnvironment, IServiceCollection services, IConfiguration configuration)
{
if (WebConsts.SwaggerUiEnabled)
{
//Swagger - Enable this line and the related lines in Configure method to enable swagger UI
services.AddSwaggerGen(options =>
{
options.SwaggerDoc(ApiNames.HostApiv1, new OpenApiInfo { Title = ApiTitles.HostApiv1, Version = "v1" });
options.SwaggerDoc(ApiNames.PartnerApiv1, new OpenApiInfo { Title = ApiTitles.PartnerApiv1, Version = "v1" });
options.SwaggerDoc(ApiNames.TenantApiv1, new OpenApiInfo { Title = ApiTitles.TenantApiv1, Version = "v1" });
OpenApiSecurityScheme securityDefinition = new OpenApiSecurityScheme()
{
Name = "Bearer",
BearerFormat = "JWT",
Scheme = "bearer",
Description = "Specify the authorization token.",
In = ParameterLocation.Header,
Type = SecuritySchemeType.Http,
};
OpenApiSecurityRequirement securityRequirements = new OpenApiSecurityRequirement()
{
{securityDefinition, new string[] { }},
};
options.AddSecurityDefinition("bearer", securityDefinition);
// Make sure swagger UI requires a Bearer token to be specified
options.AddSecurityRequirement(securityRequirements);
options.DocInclusionPredicate((docName, apiDesc) =>
{
if (!apiDesc.ActionDescriptor.IsControllerAction())
{
return false;
}
apiDesc.TryGetMethodInfo(out MethodInfo methodInfo);
var actionDocs = methodInfo.GetCustomAttributes<SwaggerDocAttribute>()
.SelectMany(a => a.IncludeInDocuments);
var controllerDocs = methodInfo.DeclaringType.GetCustomAttributes<SwaggerDocAttribute>()
.SelectMany(a => a.IncludeInDocuments);
switch (docName)
{
case ApiNames.HostApiv1:
return apiDesc.GroupName == null ||
actionDocs.Contains(ApiNames.HostApiv1) ||
controllerDocs.Contains(ApiNames.HostApiv1);
case ApiNames.PartnerApiv1:
return apiDesc.GroupName == null ||
actionDocs.Contains(ApiNames.PartnerApiv1) ||
controllerDocs.Contains(ApiNames.PartnerApiv1);
case ApiNames.TenantApiv1:
return apiDesc.GroupName == null ||
actionDocs.Contains(ApiNames.TenantApiv1) ||
controllerDocs.Contains(ApiNames.TenantApiv1);
default:
return true;
}
});
options.IgnoreObsoleteActions();
options.IgnoreObsoleteProperties();
options.OrderActionsBy((apiDesc) => $"{apiDesc.RelativePath}");
options.ParameterFilter<SwaggerEnumParameterFilter>();
options.SchemaFilter<SwaggerEnumSchemaFilter>();
options.OperationFilter<SwaggerOperationIdFilter>();
options.OperationFilter<SwaggerOperationFilter>();
options.CustomDefaultSchemaIdSelector();
options.UseAllOfToExtendReferenceSchemas();
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
options.IncludeXmlComments(xmlPath);
}).AddSwaggerGenNewtonsoftSupport();
}
}
More background information:
I am generating my API documentation into separate SwaggerDocs, for the different types of consumers of my API (Internal / Host applications; Partner applications; Normal tenant / client applications). TI decorate my ApplicationServices (which are dynamically served up as REST-like services during runtime by ABP/AspNetZero) with SwaggerDocsAttribute, which is used to delineate to which one-or-more Swagger docs should include it in its documentation.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class SwaggerDocAttribute: Attribute
{
public SwaggerDocAttribute(params string[] includeInDocuments)
{
IncludeInDocuments = includeInDocuments;
}
public string[] IncludeInDocuments { get; }
}
Example usage:
[SwaggerDoc(ApiNames.HostApiv1, ApiNames.PartnerApiv1, ApiNames.TenantApiv1)]
public class SystemStatusAppService: MyDemoAppServiceBase, ISystemStatusAppService
{
[ProducesResponseType(200, Type = typeof(SwaggerDocResponseWrapper))]
public async Task Ping()
{
//Do nothing - will return status code 200
}
[AbpAuthorize()]
[ProducesResponseType(200, Type = typeof(SwaggerDocResponseWrapper))]
public async Task PingWithAuth()
{
//Do nothing - will return status code 200
}
}
You might need to add a reference to your definition in your requirement.
OpenApiSecurityScheme securityDefinition = new OpenApiSecurityScheme()
{
BearerFormat = "JWT",
Scheme = "bearer",
Description = "Specify the authorization token.",
Type = SecuritySchemeType.Http,
};
options.AddSecurityDefinition("Bearer", securityDefinition);
OpenApiSecurityRequirement securityRequirements = new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "Bearer" }
},
new string[] {}
}
};
options.AddSecurityRequirement(securityRequirements);
There is a document describing how to allocate a public IP per VM in VMSS: https://learn.microsoft.com/en-us/azure/virtual-machine-scale-sets/virtual-machine-scale-sets-networking#public-ipv4-per-virtual-machine
But it is not clear how to assign public static IP per VM. Is it possible?
Unfortunately, Azure does not provide control of the Public IP allocation method per instance in VMSS. You can see the all supported Properties of
VirtualMachineScaleSetPublicIPAddressConfigurationProperties object in the latest ARM API version.
However, after my validation, when you restart the instance or VMSS scale-in or scale-out, the public IP address of existing instances is not changed. The public IP address of instances will be updated unless you stop the instance of VMSS.
Update
Currently, you can manage it with IpPublicPrefix. Note that IpPublicPrefix requires a standard SKU load balancer and public IP address. Here is a working sample. You can check the public IP address of the instances in VMSS with the REST API.
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"vmSku": {
"type": "string",
"defaultValue": "Standard_A1_v2",
"metadata": {
"description": "Size of VMs in the VM Scale Set."
}
},
"windowsOSVersion": {
"type": "string",
"defaultValue": "2019-Datacenter",
"allowedValues": [
"2008-R2-SP1",
"2012-Datacenter",
"2012-R2-Datacenter",
"2016-Datacenter",
"2019-Datacenter"
],
"metadata": {
"description": "The Windows version for the VM. This will pick a fully patched image of this given Windows version. Allowed values: 2008-R2-SP1, 2012-Datacenter, 2012-R2-Datacenter & 2016-Datacenter, 2019-Datacenter."
}
},
"vmssName": {
"type": "string",
"minLength": 3,
"maxLength": 61,
"metadata": {
"description": "String used as a base for naming resources. Must be 3-61 characters in length and globally unique across Azure. A hash is prepended to this string for some resources, and resource-specific information is appended."
}
},
"instanceCount": {
"type": "int",
"defaultValue": 3,
"minValue": 1,
"maxValue": 100,
"metadata": {
"description": "Number of VM instances (100 or less)."
}
},
"singlePlacementGroup": {
"type": "bool",
"defaultValue": true,
"metadata": {
"description": "When true this limits the scale set to a single placement group, of max size 100 virtual machines. NOTE: If singlePlacementGroup is true, it may be modified to false. However, if singlePlacementGroup is false, it may not be modified to true."
}
},
"adminUsername": {
"type": "string",
"defaultValue": "vmssadmin",
"metadata": {
"description": "Admin username on all VMs."
}
},
"adminPassword": {
"type": "securestring",
"metadata": {
"description": "Admin password on all VMs."
}
},
"location": {
"type": "string",
"defaultValue": "[resourceGroup().location]",
"metadata": {
"description": "Location for all resources."
}
},
"platformFaultDomainCount": {
"type": "int",
"defaultValue": 1,
"metadata": {
"description": "Fault Domain count for each placement group."
}
},
"publicIPPrefixes_pubprefix_name": {
"defaultValue": "vmsspublicprefix",
"type": "string"
}
},
"variables": {
"namingInfix": "[toLower(substring(concat(parameters('vmssName'), uniqueString(resourceGroup().id)), 0, 9))]",
"longNamingInfix": "[toLower(parameters('vmssName'))]",
"addressPrefix": "10.0.0.0/16",
"subnetPrefix": "10.0.0.0/24",
"virtualNetworkName": "[concat(variables('namingInfix'), 'vnet')]",
"publicIPAddressName": "[concat(variables('namingInfix'), 'pip')]",
"subnetName": "[concat(variables('namingInfix'), 'subnet')]",
"loadBalancerName": "[concat(variables('namingInfix'), 'lb')]",
"publicIPAddressID": "[resourceId('Microsoft.Network/publicIPAddresses',variables('publicIPAddressName'))]",
"lbProbeID": "[resourceId('Microsoft.Network/loadBalancers/probes',variables('loadBalancerName'), 'tcpProbe')]",
"natPoolName": "[concat(variables('namingInfix'), 'natpool')]",
"bePoolName": "[concat(variables('namingInfix'), 'bepool')]",
"lbPoolID": "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools',variables('loadBalancerName'),variables('bePoolName'))]",
"natStartPort": 50000,
"natEndPort": 50119,
"natBackendPort": 3389,
"nicName": "[concat(variables('namingInfix'), 'nic')]",
"ipConfigName": "[concat(variables('namingInfix'), 'ipconfig')]",
"frontEndIPConfigID": "[resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations',variables('loadBalancerName'),'loadBalancerFrontEnd')]",
"osType": {
"publisher": "MicrosoftWindowsServer",
"offer": "WindowsServer",
"sku": "[parameters('windowsOSVersion')]",
"version": "latest"
},
"imageReference": "[variables('osType')]"
},
"resources": [
{
"type": "Microsoft.Network/loadBalancers",
"apiVersion": "2020-06-01",
"name": "[variables('loadBalancerName')]",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.Network/publicIPAddresses', variables('publicIPAddressName'))]"
],
"sku": {
"name": "Standard"
},
"properties": {
"frontendIPConfigurations": [
{
"name": "LoadBalancerFrontEnd",
"properties": {
"publicIPAddress": {
"id": "[variables('publicIPAddressID')]",
"name": "Standard"
}
}
}
],
"backendAddressPools": [
{
"name": "[variables('bePoolName')]"
}
],
"inboundNatPools": [
{
"name": "[variables('natPoolName')]",
"properties": {
"frontendIPConfiguration": {
"id": "[variables('frontEndIPConfigID')]"
},
"protocol": "Tcp",
"frontendPortRangeStart": "[variables('natStartPort')]",
"frontendPortRangeEnd": "[variables('natEndPort')]",
"backendPort": "[variables('natBackendPort')]"
}
}
],
"loadBalancingRules": [
{
"name": "LBRule",
"properties": {
"frontendIPConfiguration": {
"id": "[variables('frontEndIPConfigID')]"
},
"backendAddressPool": {
"id": "[variables('lbPoolID')]"
},
"protocol": "Tcp",
"frontendPort": 80,
"backendPort": 80,
"enableFloatingIP": false,
"idleTimeoutInMinutes": 5,
"probe": {
"id": "[variables('lbProbeID')]"
}
}
}
],
"probes": [
{
"name": "tcpProbe",
"properties": {
"protocol": "Tcp",
"port": 80,
"intervalInSeconds": 5,
"numberOfProbes": 2
}
}
]
}
},
{
"type": "Microsoft.Network/publicIPPrefixes",
"apiVersion": "2020-11-01",
"name": "[parameters('publicIPPrefixes_pubprefix_name')]",
"location": "[parameters('location')]",
"sku": {
"name": "Standard",
"tier": "Regional"
},
"properties": {
"prefixLength": 28,
"publicIPAddressVersion": "IPv4",
"ipTags": []
}
},
{
"type": "Microsoft.Compute/virtualMachineScaleSets",
"apiVersion": "2020-06-01",
"name": "[variables('namingInfix')]",
"location": "[parameters('location')]",
"sku": {
"name": "[parameters('vmSku')]",
"tier": "Standard",
"capacity": "[parameters('instanceCount')]"
},
"dependsOn": [
"[resourceId('Microsoft.Network/loadBalancers', variables('loadBalancerName'))]",
"[resourceId('Microsoft.Network/virtualNetworks', variables('virtualNetworkName'))]",
"[resourceId('Microsoft.Network/publicIPPrefixes',parameters('publicIPPrefixes_pubprefix_name'))]"
],
"properties": {
"overprovision": true,
"upgradePolicy": {
"mode": "Automatic"
},
"singlePlacementGroup": "[parameters('singlePlacementGroup')]",
"platformFaultDomainCount": "[parameters('platformFaultDomainCount')]",
"virtualMachineProfile": {
"storageProfile": {
"osDisk": {
"caching": "ReadWrite",
"createOption": "FromImage"
},
"imageReference": "[variables('imageReference')]"
},
"osProfile": {
"computerNamePrefix": "[variables('namingInfix')]",
"adminUsername": "[parameters('adminUsername')]",
"adminPassword": "[parameters('adminPassword')]"
},
"networkProfile": {
"networkInterfaceConfigurations": [
{
"name": "[variables('nicName')]",
"properties": {
"primary": true,
"ipConfigurations": [
{
"name": "[variables('ipConfigName')]",
"properties": {
"subnet": {
"id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('virtualNetworkName'), variables('subnetName'))]"
},
"loadBalancerBackendAddressPools": [
{
"id": "[variables('lbPoolID')]"
}
],
"loadBalancerInboundNatPools": [
{
"id": "[resourceId('Microsoft.Network/loadBalancers/inboundNatPools', variables('loadBalancerName'), variables('natPoolName'))]"
}
],
"publicipaddressconfiguration": {
"name": "pub1",
"properties": {
"idleTimeoutInMinutes": 15,
"publicIPAddressVersion": "IPv4",
"publicIPPrefix":{
"id": "[resourceId('Microsoft.Network/publicIPPrefixes',parameters('publicIPPrefixes_pubprefix_name'))]"
}
}
}
}
}
]
}
}
]
}
}
}
},
{
"type": "Microsoft.Network/publicIPAddresses",
"apiVersion": "2020-06-01",
"name": "[variables('publicIPAddressName')]",
"location": "[parameters('location')]",
"sku": {
"name": "Standard"
},
"properties": {
"publicIPAllocationMethod": "Static",
"dnsSettings": {
"domainNameLabel": "[variables('longNamingInfix')]"
}
}
},
{
"type": "Microsoft.Network/virtualNetworks",
"apiVersion": "2020-06-01",
"name": "[variables('virtualNetworkName')]",
"location": "[parameters('location')]",
"properties": {
"addressSpace": {
"addressPrefixes": [
"[variables('addressPrefix')]"
]
},
"subnets": [
{
"name": "[variables('subnetName')]",
"properties": {
"addressPrefix": "[variables('subnetPrefix')]"
}
}
]
}
},
{
"type": "Microsoft.Insights/autoscaleSettings",
"apiVersion": "2015-04-01",
"name": "autoscalehost",
"location": "[parameters('location')]",
"dependsOn": [
"[resourceId('Microsoft.Compute/virtualMachineScaleSets/', variables('namingInfix'))]"
],
"properties": {
"name": "autoscalehost",
"targetResourceUri": "[resourceId('Microsoft.Compute/virtualMachineScaleSets', variables('namingInfix'))]",
"enabled": true,
"profiles": [
{
"name": "Profile1",
"capacity": {
"minimum": "1",
"maximum": "10",
"default": "1"
},
"rules": [
{
"metricTrigger": {
"metricName": "Percentage CPU",
"metricResourceUri": "[resourceId('Microsoft.Compute/virtualMachineScaleSets', variables('namingInfix'))]",
"timeGrain": "PT1M",
"statistic": "Average",
"timeWindow": "PT5M",
"timeAggregation": "Average",
"operator": "GreaterThan",
"threshold": 50
},
"scaleAction": {
"direction": "Increase",
"type": "ChangeCount",
"value": "1",
"cooldown": "PT5M"
}
},
{
"metricTrigger": {
"metricName": "Percentage CPU",
"metricResourceUri": "[resourceId('Microsoft.Compute/virtualMachineScaleSets', variables('namingInfix'))]",
"timeGrain": "PT1M",
"statistic": "Average",
"timeWindow": "PT5M",
"timeAggregation": "Average",
"operator": "LessThan",
"threshold": 30
},
"scaleAction": {
"direction": "Decrease",
"type": "ChangeCount",
"value": "1",
"cooldown": "PT5M"
}
}
]
}
]
}
}
]
}
Trying to deploy a CloudFormation template gets stuck with 'AWS::ECS::Service' stuck on 'CREATE_IN_PROGRESS' with status 'Resource creation Initiated'.
Can anyone please tell me if there's a configuration problem with my CloudFormation template?
How would i troubleshoot this?
CloudFormation Template
Thank you!
"FargateTasksCluster": {
"Type": "AWS::ECS::Cluster"
},
"FargateTasksLogGroup": {
"Type": "AWS::Logs::LogGroup"
},
"MytaskTask": {
"Type": "AWS::ECS::TaskDefinition",
"Properties": {
"ContainerDefinitions": [{
"Name": "my-task",
"Image": "mountainpass/infrastructure:helloworld",
"Environment": [],
"LogConfiguration": {
"LogDriver": "awslogs",
"Options": {
"awslogs-region": {
"Fn::Sub": "${AWS::Region}"
},
"awslogs-group": {
"Fn::Sub": "${FargateTasksLogGroup}"
},
"awslogs-stream-prefix": "fargate"
}
}
}],
"Family": "my-task",
"NetworkMode": "awsvpc",
"ExecutionRoleArn": {
"Fn::Sub": "arn:aws:iam::${AWS::AccountId}:role/ecsTaskExecutionRole"
},
"TaskRoleArn": {
"Fn::Sub": "${IamRoleLambdaExecution}"
},
"RequiresCompatibilities": ["FARGATE"],
"Memory": "0.5GB",
"Cpu": 256
}
},
"MytaskService": {
"Type": "AWS::ECS::Service",
"Properties": {
"Cluster": {
"Fn::Sub": "${FargateTasksCluster}"
},
"LaunchType": "FARGATE",
"ServiceName": "my-task",
"DesiredCount": 1,
"TaskDefinition": {
"Fn::Sub": "${MytaskTask}"
},
"NetworkConfiguration": {
"AwsvpcConfiguration": {
"AssignPublicIp": "DISABLED",
"SecurityGroups": [],
"Subnets": ["subnet-2cc37974", "subnet-3c959d5b", "subnet-49d7d200"]
}
}
}
}
I am new to crossbar/autobahn framework and to network programming in general, recently at work we would like to experiment to move from WebSocket transport to RawSocket Transport with using MessagePackSerializer and do performance comparison. I am requesting to the framework developers to please guide me to convert following config to use raw socket. will using auth settings from ws section should work ?
{
"version": 2,
"workers": [
{
"type": "router",
"options": {
"pythonpath": ["../components"]
},
"realms": [
{
"name": "realm1",
"roles": [
{
"name": "system",
"authorizer": "com.XXXX.auth.authorize"
},
{
"name": "user",
"authorizer": "com.XXXX.auth.authorize"
},
{
"name": "service",
"authorizer": "com.XXXX.auth.authorize"
}
]
}
],
"transports": [
{
"type": "rawsocket",
"endpoint": {
"type": "unix",
"path": "/tmp/crossbar-socket"
}
},
{
"type": "web",
"endpoint": {
"type": "tcp",
"port": 8080
},
"paths": {
"status": {
"type": "resource",
"classname": "health_web_indicator.run",
"extra": {
"DVMT_VERSION": "2.0.1",
"HEALTHY_INDICATOR": ":) DVMT UP",
"UNHEALTHY_INDICATOR": ":( DVMT DOWN",
"MSSQL_DRIVER": "FreeTDS",
"MSSQL_SERVER": "XXXX.dv.local",
"MSSQL_PORT": "1515",
"MSSQL_DATABASE": "XXXX",
"MSSQL_USER": "XXXX",
"MSSQL_PASSWORD": "XXXX",
"SUPERVISOR_PROCESSES": ["XXXX", "XXXX", "XXXX", "XXXX", "XXXX"],
"SUPERVISOR_SERVER_SOCKET": "/crossbar/supervisord.sock"
}
},
"ws": {
"type": "websocket",
"auth": {
"anonymous": {
"type": "dynamic",
"authenticator": "com.XXXX.auth.authenticate"
},
"ticket": {
"type": "dynamic",
"authenticator": "com.XXXX.auth.authenticate"
}
},
"debug": true,
"options": {
"auto_ping_interval": 0,
"auto_ping_timeout": 60000,
"auto_ping_size": 4,
"compression": {
"deflate": {
"request_no_context_takeover": false,
"request_max_window_bits": 11,
"no_context_takeover": false,
"max_window_bits": 11,
"memory_level": 4
}
}
}
}
}
}
],
"components": [
{
"id": "auth",
"type": "class",
"classname": "auth.AuthComponent",
"realm": "realm1",
"role": "trusted",
"extra": {
"prefix": "com.XXXX.auth",
"system_role": "system",
"anonymous_role": "anonymous",
"mssql": {
"driver": "FreeTDS",
"server": "XXXX.dv.local",
"port": "1515",
"database": "XXXX",
"uid": "XXXX",
"pwd": "XXXX",
"autocommit": true
},
"heartbeat_rate": 30.0,
"ldap_api_url": "http://XXXX:80/rssapi/dvmsapi/admin/GetUserLdapGroups/{username}",
"enforce_cert_match": false,
"cert_sub_pattern": "[\\s_-]"
}
}
]
}
]
}
also will it be easy enough for you to please give me an example for using autobahnpython as a client for rawsocket transport. https://crossbar.io/docs/RawSocket-Transport/#example---connecting-transport
I am trying to create a SAM template (serverless.template) in visual studio to publish my API Gateway. I have a couple of errors being produced within the template validation (in Visual Studio) that I'm unable to resolve. When published, the stack deploys, but there is no usage plan(s) or api key(s) created (or rather, they are created but not viewable in the console, they come up as 'invalid reference' if you try to view them via Cloudformation > Resources, and they don't show up at all in the API Gateway console).
The errors are:
ServerlessRestApiDeployment26aad1646f is an unknown reference
"ServerlessRestApiProdStage": {
"Type": "AWS::ApiGateway::Stage",
"Properties": {
"DeploymentId": {
"Ref": "ServerlessRestApiDeployment26aad1646f"
},
"RestApiId": {
"Ref": "ServerlessRestApi"
},
"StageName": "Prod"
}
},
And
ServerlessRestApiProdStage is an invalid type for this reference
"APIGatewayHeartInHandKey": {
"Type": "AWS::ApiGateway::ApiKey",
"DependsOn": [
"ServerlessRestApi",
"ServerlessRestApiProdStage"
],
"Properties": {
"Name": "HeartInHandApiKey",
"Description": "Api Key for Heart In Hand",
"Enabled": true,
"GenerateDistinctId": true,
"StageKeys": [
{
"RestApiId": {
"Ref": "ServerlessRestApi"
},
"StageName": {
"Ref": "ServerlessRestApiProdStage"
}
}
]
}
},
The full SAM template shown below.
{
"AWSTemplateFormatVersion": "2010-09-09",
"Transform": "AWS::Serverless-2016-10-31",
"Description": "API Gateway to access InSite data-store",
"Resources": {
"Get": {
"Type": "AWS::Serverless::Function",
"Properties": {
"VpcConfig": {
"SecurityGroupIds": [
"sg-111a1476"
],
"SubnetIds": [
"subnet-3029a769",
"subnet-5ec0b928"
]
},
"Handler": "AWSServerlessInSiteDataGw::AWSServerlessInSiteDataGw.Functions::Get",
"Runtime": "dotnetcore2.0",
"CodeUri": "",
"MemorySize": 256,
"Timeout": 30,
"Role": null,
"Policies": [
"AWSLambdaBasicExecutionRole",
"AWSLambdaVPCAccessExecutionRole",
"AmazonSSMFullAccess"
],
"Events": {
"PutResource": {
"Type": "Api",
"Properties": {
"Path": "/",
"Method": "GET"
}
}
}
}
},
"GetTableBasic": {
"Type": "AWS::Serverless::Function",
"Properties": {
"VpcConfig": {
"SecurityGroupIds": [
"sg-111a1476"
],
"SubnetIds": [
"subnet-3029a769",
"subnet-5ec0b928"
]
},
"Handler": "AWSServerlessInSiteDataGw::AWSServerlessInSiteDataGw.Functions::GetTableBasic",
"Runtime": "dotnetcore2.0",
"CodeUri": "",
"MemorySize": 256,
"Timeout": 30,
"Role": null,
"Policies": [
"AWSLambdaBasicExecutionRole",
"AWSLambdaVPCAccessExecutionRole",
"AmazonSSMFullAccess"
],
"Events": {
"PutResource": {
"Type": "Api",
"Properties": {
"Path": "/tables/{tableid}/{columnid}",
"Method": "GET"
}
}
}
}
},
"GetColumnList": {
"Type": "AWS::Serverless::Function",
"Properties": {
"VpcConfig": {
"SecurityGroupIds": [
"sg-111a1476"
],
"SubnetIds": [
"subnet-3029a769",
"subnet-5ec0b928"
]
},
"Handler": "AWSServerlessInSiteDataGw::AWSServerlessInSiteDataGw.Functions::GetColumnList",
"Runtime": "dotnetcore2.0",
"CodeUri": "",
"MemorySize": 256,
"Timeout": 30,
"Role": null,
"Policies": [
"AWSLambdaBasicExecutionRole",
"AWSLambdaVPCAccessExecutionRole",
"AmazonSSMFullAccess"
],
"Events": {
"PutResource": {
"Type": "Api",
"Properties": {
"Path": "/list/columns/{tableid}",
"Method": "GET"
}
}
}
}
},
"GetTableList": {
"Type": "AWS::Serverless::Function",
"Properties": {
"VpcConfig": {
"SecurityGroupIds": [
"sg-111a1476"
],
"SubnetIds": [
"subnet-3029a769",
"subnet-5ec0b928"
]
},
"Handler": "AWSServerlessInSiteDataGw::AWSServerlessInSiteDataGw.Functions::GetTableList",
"Runtime": "dotnetcore2.0",
"CodeUri": "",
"MemorySize": 256,
"Timeout": 30,
"Role": null,
"Policies": [
"AWSLambdaBasicExecutionRole",
"AWSLambdaVPCAccessExecutionRole",
"AmazonSSMFullAccess"
],
"Events": {
"PutResource": {
"Type": "Api",
"Properties": {
"Path": "/list/tables",
"Method": "GET"
}
}
}
}
},
"PostClickCollectNotification": {
"Type": "AWS::Serverless::Function",
"Properties": {
"VpcConfig": {
"SecurityGroupIds": [
"sg-111a1476"
],
"SubnetIds": [
"subnet-3029a769",
"subnet-5ec0b928"
]
},
"Handler": "AWSServerlessInSiteDataGw::AWSServerlessInSiteDataGw.Functions::PostClickCollectNotification",
"Runtime": "dotnetcore2.0",
"CodeUri": "",
"MemorySize": 256,
"Timeout": 30,
"Role": null,
"Policies": [
"AWSLambdaBasicExecutionRole",
"AWSLambdaVPCAccessExecutionRole",
"AmazonSSMFullAccess"
],
"Events": {
"PutResource": {
"Type": "Api",
"Properties": {
"Path": "/datagw/general/webhook/ccnotify",
"Method": "POST"
}
}
}
}
},
"PostClickCollectStockUpdate": {
"Type": "AWS::Serverless::Function",
"Properties": {
"VpcConfig": {
"SecurityGroupIds": [
"sg-111a1476"
],
"SubnetIds": [
"subnet-3029a769",
"subnet-5ec0b928"
]
},
"Handler": "AWSServerlessInSiteDataGw::AWSServerlessInSiteDataGw.Functions::PostClickCollectStockUpdate",
"Runtime": "dotnetcore2.0",
"CodeUri": "",
"MemorySize": 256,
"Timeout": 30,
"Role": null,
"Policies": [
"AWSLambdaBasicExecutionRole",
"AWSLambdaVPCAccessExecutionRole",
"AmazonSSMFullAccess"
],
"Events": {
"PutResource": {
"Type": "Api",
"Properties": {
"Path": "/datagw/general/post/sohupdate",
"Method": "POST"
}
}
}
}
},
"GetTableResponse": {
"Type": "AWS::Serverless::Function",
"Properties": {
"VpcConfig": {
"SecurityGroupIds": [
"sg-111a1476"
],
"SubnetIds": [
"subnet-3029a769",
"subnet-5ec0b928"
]
},
"Handler": "AWSServerlessInSiteDataGw::AWSServerlessInSiteDataGw.Functions::GetTableResponse",
"Runtime": "dotnetcore2.0",
"CodeUri": "",
"MemorySize": 256,
"Timeout": 30,
"Role": null,
"Policies": [
"AWSLambdaBasicExecutionRole",
"AWSLambdaVPCAccessExecutionRole",
"AmazonSSMFullAccess"
],
"Events": {
"PutResource": {
"Type": "Api",
"Properties": {
"Path": "datagw/general/table/get/{tableid}",
"Method": "GET"
}
}
}
}
},
"ServerlessRestApi": {
"Type": "AWS::ApiGateway::RestApi",
"Properties": {
"Description":"This is a placeholder for the description of this web api",
"ApiKeySourceType":"HEADER",
"Body": {
"info": {
"version": "1.0",
"title": {
"Ref": "AWS::StackName"
}
},
"paths": {
"/list/tables": {
"get": {
"x-amazon-apigateway-integration": {
"httpMethod": "POST",
"type": "aws_proxy",
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetTableList.Arn}/invocations"
}
},
"responses": {}
}
},
"/list/columns/{tableid}": {
"get": {
"x-amazon-apigateway-integration": {
"httpMethod": "POST",
"type": "aws_proxy",
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetColumnList.Arn}/invocations"
}
},
"responses": {}
}
},
"datagw/general/table/get/{tableid}": {
"get": {
"x-amazon-apigateway-integration": {
"httpMethod": "POST",
"type": "aws_proxy",
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetTableResponse.Arn}/invocations"
}
},
"responses": {}
}
},
"/": {
"get": {
"x-amazon-apigateway-integration": {
"httpMethod": "POST",
"type": "aws_proxy",
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${Get.Arn}/invocations"
}
},
"responses": {}
}
},
"/tables/{tableid}/{columnid}": {
"get": {
"x-amazon-apigateway-integration": {
"httpMethod": "POST",
"type": "aws_proxy",
"uri": {
"Fn::Sub": "arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${GetTableBasic.Arn}/invocations"
}
},
"responses": {}
}
}
},
"swagger": "2.0"
}
}
},
"ServerlessRestApiProdStage": {
"Type": "AWS::ApiGateway::Stage",
"Properties": {
"DeploymentId": {
"Ref": "ServerlessRestApiDeployment26aad1646f"
},
"RestApiId": {
"Ref": "ServerlessRestApi"
},
"StageName": "Prod"
}
},
"CustomLambdaExecutionRole": {
"Type": "AWS::IAM::Role",
"Properties": {
"AssumeRolePolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com"
]
},
"Action": [
"sts:AssumeRole"
]
}
]
},
"Policies": [
{
"PolicyName": "lambdaAccessApiKeys",
"PolicyDocument": {
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"apigateway:GET"
],
"Resource": {
"Fn::Sub": [
"arn:aws:apigateway:ap-southeast-2::/apikeys/${__keyId__}",
{
"__keyId__": {
"Ref": "APIGatewayHeartInHandKey"
}
}
]
}
}
]
}
}
]
}
},
"GetApiKeyValueLambdaFunction": {
"Type": "AWS::Lambda::Function",
"Properties": {
"Code": {
"ZipFile": {
"Fn::Join": [
"\n",
[
"import json",
"import boto3",
"client = boto3.client('apigateway')",
"def lambda_handler(event, context):",
" response = client.get_api_key(",
" apiKey= event['apiKeyId'],",
" includeValue = True",
")",
" return {",
" 'statusCode': 200,",
" 'body': response['value']}"
]
]
}
},
"Handler": "index.lambda_handler",
"Runtime": "python3.6",
"Timeout": 30,
"Role": {
"Fn::GetAtt": [
"CustomLambdaExecutionRole",
"Arn"
]
}
}
},
"APIGatewayUsagePlanInternal": {
"Type": "AWS::ApiGateway::UsagePlan",
"Properties": {
"ApiStages": [
{
"ApiId": {
"Ref": "ServerlessRestApi"
},
"Stage": {
"Ref": "ServerlessRestApiProdStage"
}
}
],
"Description": "Internal Apps Usage Plan",
"UsagePlanName": "Insite-datagw-InternalAppPlan"
}
},
"APIGatewayUsagePlanExternal": {
"Type": "AWS::ApiGateway::UsagePlan",
"Properties": {
"ApiStages": [
{
"ApiId": {
"Ref": "ServerlessRestApi"
},
"Stage": {
"Ref": "ServerlessRestApiProdStage"
}
}
],
"Description": "External Apps Usage Plan",
"UsagePlanName": "InSite-datagw-ExternalAppPlan"
}
},
"APIGatewayHeartInHandKey": {
"Type": "AWS::ApiGateway::ApiKey",
"DependsOn": [
"ServerlessRestApi",
"ServerlessRestApiProdStage"
],
"Properties": {
"Name": "HeartInHandApiKey",
"Description": "Api Key for Heart In Hand",
"Enabled": true,
"GenerateDistinctId": true,
"StageKeys": [
{
"RestApiId": {
"Ref": "ServerlessRestApi"
},
"StageName": {
"Ref": "ServerlessRestApiProdStage"
}
}
]
}
},
"LinkHeartInHandKey": {
"Type": "AWS::ApiGateway::UsagePlanKey",
"Properties": {
"KeyId": {
"Ref": "APIGatewayHeartInHandKey"
},
"KeyType": "API_KEY",
"UsagePlanId": {
"Ref": "APIGatewayUsagePlanInternal"
}
}
},
"APIGatewayPricelineSiteKey": {
"Type": "AWS::ApiGateway::ApiKey",
"DependsOn": [
"ServerlessRestApi",
"ServerlessRestApiProdStage"
],
"Properties": {
"Name": "PricelineSiteApiKey",
"Description": "Api Key for Priceline Website",
"Enabled": true,
"GenerateDistinctId": true,
"StageKeys": [
{
"RestApiId": {
"Ref": "ServerlessRestApi"
},
"StageName": {
"Ref": "ServerlessRestApiProdStage"
}
}
]
}
},
"LinkPricelineSiteKey": {
"Type": "AWS::ApiGateway::UsagePlanKey",
"Properties": {
"KeyId": {
"Ref": "APIGatewayPricelineSiteKey"
},
"KeyType": "API_KEY",
"UsagePlanId": {
"Ref": "APIGatewayUsagePlanInternal"
}
}
}
},
"Outputs": {
"ApiURL": {
"Description": "API endpoint URL for Prod environment",
"Value": {
"Fn::Sub": "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/"
}
}
}
}
it looks like you're trying to use features of the AWS::Serverless::Api resource from SAM without defining an AWS::Serverless::Api in your template.
In order to fix the issues you've brought up, it looks like you need to:
Remove the ServerlessRestApiProdStage resource
Sam will generate this resource (and the deployment resource) for you if you use an AWS::Serverless::Api resource.
Convert your AWS::ApiGateway::RestApi resource into an AWS::Serverless::Api resource:
Remove ApiKeySourceType property and add "x-amazon-apigateway-api-key-source" : "HEADER", to the swagger,
Change the Type from AWS::ApiGateway::RestApi to AWS::Serverless::Api
"ServerlessRestApi": {
"Type": "AWS::Serverless::RestApi",
"Properties": {
"Description":"This is a placeholder for the description of this web api",
"Body": {
"info": {
"version": "1.0",
"title": {
"Ref": "AWS::StackName"
}
},
"x-amazon-apigateway-api-key-source" : "HEADER",
"paths": {
...