cloudmason 1.0.12 → 1.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/commands/helpers/cf.js +4 -5
- package/commands/helpers/params.js +22 -0
- package/commands/helpers/stacks/asg.yaml +10 -9
- package/commands/helpers/stacks/infra.yaml +3 -14
- package/commands/init_org.js +6 -6
- package/commands/publish.js +198 -0
- package/main.js +20 -3
- package/package.json +1 -1
package/commands/helpers/cf.js
CHANGED
|
@@ -61,11 +61,10 @@ exports.deployS3Stack = async function(stackName,s3Url,params,tag,region){
|
|
|
61
61
|
return result.StackId;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
exports.updateOrgStack = async function(
|
|
65
|
-
const client = new CloudFormationClient({ region });
|
|
66
|
-
const cfParams = Object.keys(params).map(k=>{ return { ParameterKey: k, ParameterValue: params[k] } })
|
|
64
|
+
exports.updateOrgStack = async function(yamlPath){
|
|
65
|
+
const client = new CloudFormationClient({ region: process.env.orgRegion });
|
|
67
66
|
|
|
68
|
-
const stackPath = path.resolve(
|
|
67
|
+
const stackPath = path.resolve(yamlPath);
|
|
69
68
|
if (!fs.existsSync(stackPath)){
|
|
70
69
|
console.log('Infra Stack not found');
|
|
71
70
|
throw { message: 'Infra stack not found', at: 'deployStack'}
|
|
@@ -75,11 +74,11 @@ exports.updateOrgStack = async function(region,params){
|
|
|
75
74
|
const cmd = {
|
|
76
75
|
StackName: 'CoreInfra',
|
|
77
76
|
TemplateBody: stackYML,
|
|
78
|
-
Parameters: cfParams,
|
|
79
77
|
Capabilities: ["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM"]
|
|
80
78
|
};
|
|
81
79
|
const command = new UpdateStackCommand(cmd);
|
|
82
80
|
const response = await client.send(command);
|
|
81
|
+
console.log('Update response',response);
|
|
83
82
|
return response.StackId;
|
|
84
83
|
}
|
|
85
84
|
|
|
@@ -105,6 +105,28 @@ exports.updateAppV = async function(appName,version,vParams){
|
|
|
105
105
|
}
|
|
106
106
|
|
|
107
107
|
// INSTANCE PARAMS
|
|
108
|
+
exports.setOrgParams = async function(orgName,VpcId,repo){
|
|
109
|
+
if (!process.env.orgRegion){ throw new Error('Region not set') }
|
|
110
|
+
const r1 = await writeParam('/infra/org_name',orgName);
|
|
111
|
+
console.log('Set Org Name:',r1)
|
|
112
|
+
const r2 = await writeParam('/infra/vpc_id',VpcId);
|
|
113
|
+
console.og('Set VPC ID:',r2);
|
|
114
|
+
const r3 = await writeParam('/infra/GitHubRepoName',repo || '');
|
|
115
|
+
console.log('Set GitHub Repo:',r3)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
exports.addPid = async function(appName,productId){
|
|
120
|
+
if (!process.env.orgRegion){ throw new Error('Region not set') }
|
|
121
|
+
const appKey = `/infra/apps/${appName.toLowerCase()}`
|
|
122
|
+
const appStr = await readParam(appKey,process.env.orgRegion);
|
|
123
|
+
const app = JSON.parse(appStr);
|
|
124
|
+
|
|
125
|
+
app.pid = productId;
|
|
126
|
+
await writeParam(appKey,JSON.stringify(app),process.env.orgRegion);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
|
|
108
130
|
exports.addInstance = async function(appName,instanceName,params){
|
|
109
131
|
if (!process.env.orgRegion){ throw new Error('Region not set') }
|
|
110
132
|
const appKey = `/infra/apps/${appName.toLowerCase()}`
|
|
@@ -20,23 +20,24 @@ Parameters:
|
|
|
20
20
|
Type: Number
|
|
21
21
|
Description: Max number of Ec2 instances
|
|
22
22
|
Default: 2
|
|
23
|
-
EC2InstanceType:
|
|
24
|
-
Type: String
|
|
25
|
-
Description: EC2 Instance Type
|
|
26
|
-
Default: t2.small
|
|
27
23
|
AdminEmail:
|
|
28
24
|
Type: String
|
|
29
25
|
Description: Email for the first admin user
|
|
30
|
-
AmiId:
|
|
31
|
-
Type: AWS::EC2::Image::Id
|
|
32
|
-
Description: Max number of Ec2 instances
|
|
33
26
|
AppVersion:
|
|
34
27
|
Type: String
|
|
35
28
|
Description: Major.minor.build
|
|
36
29
|
InstanceEnvironment:
|
|
37
30
|
Type: String
|
|
38
31
|
Description: Instance enviroment (prod,dev). Setting prod will enable advanced security features.
|
|
39
|
-
|
|
32
|
+
#-Strip
|
|
33
|
+
EC2InstanceType:
|
|
34
|
+
Type: String
|
|
35
|
+
Description: EC2 Instance Type
|
|
36
|
+
Default: m6a.large
|
|
37
|
+
AmiId:
|
|
38
|
+
Type: AWS::EC2::Image::Id
|
|
39
|
+
Description: Max number of Ec2 instances
|
|
40
|
+
#-Strip
|
|
40
41
|
|
|
41
42
|
Resources:
|
|
42
43
|
# ACM Domain
|
|
@@ -196,7 +197,7 @@ Resources:
|
|
|
196
197
|
source start.sh
|
|
197
198
|
ImageId: !Ref AmiId
|
|
198
199
|
DisableApiTermination: "true"
|
|
199
|
-
InstanceType:
|
|
200
|
+
InstanceType: m6a.large
|
|
200
201
|
SecurityGroupIds:
|
|
201
202
|
- !Ref AppEc2SecurityGroup
|
|
202
203
|
AppEc2SecurityGroup:
|
|
@@ -2,15 +2,10 @@ AWSTemplateFormatVersion: '2010-09-09'
|
|
|
2
2
|
Description: Org Cloudformation Template
|
|
3
3
|
|
|
4
4
|
Parameters:
|
|
5
|
-
orgName:
|
|
6
|
-
Type: String
|
|
7
|
-
Description: Unique name of organization
|
|
8
|
-
VpcId:
|
|
9
|
-
Type: String
|
|
10
|
-
Description: Org VPC
|
|
11
5
|
GitHubRepoName:
|
|
12
|
-
Type: String
|
|
6
|
+
Type: AWS::SSM::Parameter::Value<String>
|
|
13
7
|
Description: "GitHub repository name in the format owner/repo"
|
|
8
|
+
Default: /infra/GitHubRepoName
|
|
14
9
|
|
|
15
10
|
|
|
16
11
|
Resources:
|
|
@@ -60,7 +55,7 @@ Resources:
|
|
|
60
55
|
SecurityGroupEgress:
|
|
61
56
|
- IpProtocol: '-1'
|
|
62
57
|
CidrIp: '0.0.0.0/0'
|
|
63
|
-
VpcId:
|
|
58
|
+
VpcId: '{{resolve:ssm:/infra/vpc_id}}'
|
|
64
59
|
# Bucket Policy
|
|
65
60
|
InfraBucketPolicy:
|
|
66
61
|
Type: AWS::S3::BucketPolicy
|
|
@@ -79,12 +74,6 @@ Resources:
|
|
|
79
74
|
- !Sub arn:aws:s3:::${InfraBucket}
|
|
80
75
|
- !Sub arn:aws:s3:::${InfraBucket}/*
|
|
81
76
|
# Org Params
|
|
82
|
-
ParamOrgInfo:
|
|
83
|
-
Type: 'AWS::SSM::Parameter'
|
|
84
|
-
Properties:
|
|
85
|
-
Name: '/infra/orgName'
|
|
86
|
-
Type: 'String'
|
|
87
|
-
Value: !Ref orgName
|
|
88
77
|
ParamInstanceProfile:
|
|
89
78
|
Type: 'AWS::SSM::Parameter'
|
|
90
79
|
Properties:
|
package/commands/init_org.js
CHANGED
|
@@ -4,7 +4,7 @@ const { S3Client,HeadBucketCommand } = require("@aws-sdk/client-s3");
|
|
|
4
4
|
const { EC2Client, DescribeVpcsCommand } = require("@aws-sdk/client-ec2");
|
|
5
5
|
|
|
6
6
|
const CF = require('./helpers/cf');
|
|
7
|
-
|
|
7
|
+
const Params = require('./helpers/params');
|
|
8
8
|
|
|
9
9
|
exports.main = async function(args){
|
|
10
10
|
console.log(`Setting up ${args.name}@ in ${args.region} with repo ${args.repo}`)
|
|
@@ -13,6 +13,9 @@ exports.main = async function(args){
|
|
|
13
13
|
const VpcId = await getDefaultVPC(args.region);
|
|
14
14
|
console.log(`Default VPC: ${VpcId}`);
|
|
15
15
|
|
|
16
|
+
// Set Param
|
|
17
|
+
await Params.setOrgParams(args.name,VpcId,args.repo);
|
|
18
|
+
|
|
16
19
|
// Deploy Stack
|
|
17
20
|
const success = await CF.deployOrgStack(args.region, {orgName: args.name, VpcId: VpcId, GitHubRepoName: args.repo})
|
|
18
21
|
if (success === false){
|
|
@@ -29,14 +32,11 @@ exports.main = async function(args){
|
|
|
29
32
|
}
|
|
30
33
|
|
|
31
34
|
exports.updateOrgStack = async function(args){
|
|
32
|
-
console.log(`Updating
|
|
35
|
+
console.log(`Updating Org Stack from ${args.stack}`)
|
|
33
36
|
|
|
34
|
-
// Get VPC ID
|
|
35
|
-
const VpcId = await getDefaultVPC(args.region);
|
|
36
|
-
console.log(`Default VPC: ${VpcId}`);
|
|
37
37
|
|
|
38
38
|
// Deploy Stack
|
|
39
|
-
const success = await CF.updateOrgStack(args.
|
|
39
|
+
const success = await CF.updateOrgStack(args.stack)
|
|
40
40
|
if (success === false){
|
|
41
41
|
console.log('ERR:', success);
|
|
42
42
|
throw new Error('Unknown error updating org stack')
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
const { MarketplaceCatalogClient, StartChangeSetCommand,DescribeChangeSetCommand, DescribeEntityCommand} = require("@aws-sdk/client-marketplace-catalog");
|
|
2
|
+
const { EC2Client, DescribeImagesCommand } = require("@aws-sdk/client-ec2");
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const Params = require('./helpers/params');
|
|
6
|
+
|
|
7
|
+
exports.add_listing = async function(args){
|
|
8
|
+
console.log('Adding Listing>>', args.app, args.pid);
|
|
9
|
+
await Params.addPid(args.app,args.pid.trim());
|
|
10
|
+
const app = await Params.getApp(args.app);
|
|
11
|
+
console.log('Added listing:',args.app, app.pid);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
exports.main = async function(args){
|
|
15
|
+
// -- Get Version & Descriptions
|
|
16
|
+
const pubArgs = {
|
|
17
|
+
version: args.v,
|
|
18
|
+
changeDescription: args.desc
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// -- Get Params
|
|
22
|
+
const app = await Params.getApp(args.app);
|
|
23
|
+
pubArgs.productId = app.pid;
|
|
24
|
+
const instanceVersion = app.versions[pubArgs.version];
|
|
25
|
+
if (!instanceVersion){
|
|
26
|
+
console.log('ERR: Version not found:',pubArgs.version);
|
|
27
|
+
throw new Error('Version not found:' + pubArgs.version);
|
|
28
|
+
}
|
|
29
|
+
pubArgs.amiId = instanceVersion.baseAMI_Id;
|
|
30
|
+
console.log('Publishing AMI:\n\t',Object.entries(pubArgs).map(([k,v])=>{return `${k}:${v}`}).join('\n\t'));
|
|
31
|
+
console.log('----------')
|
|
32
|
+
|
|
33
|
+
// -- Publish AMI to Marketplace
|
|
34
|
+
await updateAmiVersion(pubArgs);
|
|
35
|
+
|
|
36
|
+
// -- Get Marketplace AMI IDs
|
|
37
|
+
// AmiAlias: '/aws/service/marketplace/prod-shmtmk4gqrfge/1.2'
|
|
38
|
+
const amiAlias = `/aws/service/marketplace/${pubArgs.productId}/${pubArgs.version}`;
|
|
39
|
+
let stackTxt = fs.readFileSync(path.resolve(args.stack),'utf8');
|
|
40
|
+
stackTxt = stackTxt.replace(`ImageId: !Ref AmiId`,`ImageId: resolve:ssm:${amiAlias}`);
|
|
41
|
+
stackTxt = stackTxt.replace(/^#-Strip.+#-Strip/ms,'');
|
|
42
|
+
|
|
43
|
+
// -- Update CF Template with AMI IDs
|
|
44
|
+
const newFileName = path.resolve(args.out);
|
|
45
|
+
console.log('Updating Template:',newFileName);
|
|
46
|
+
fs.writeFileSync(newFileName,stackTxt);
|
|
47
|
+
return true
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
// Update AMI Function
|
|
54
|
+
|
|
55
|
+
const updateAmiVersion = async ({productId, amiId, version, changeDescription}) => {
|
|
56
|
+
const client = new MarketplaceCatalogClient({ region: process.env.orgRegion }); // Update the region if needed
|
|
57
|
+
console.log('Updating AMI version:',productId, amiId, version, changeDescription);
|
|
58
|
+
try {
|
|
59
|
+
// Define the change set to update the AMI version
|
|
60
|
+
const changeSet = {
|
|
61
|
+
Catalog: "AWSMarketplace",
|
|
62
|
+
Intent: "VALIDATE",
|
|
63
|
+
ChangeSet: [
|
|
64
|
+
{
|
|
65
|
+
ChangeType: "AddDeliveryOptions",
|
|
66
|
+
Entity: {
|
|
67
|
+
Type: "AmiProduct@1.0",
|
|
68
|
+
Identifier: productId,
|
|
69
|
+
},
|
|
70
|
+
Details: JSON.stringify({
|
|
71
|
+
Version: {
|
|
72
|
+
VersionTitle: version,
|
|
73
|
+
ReleaseNotes: changeDescription,
|
|
74
|
+
},
|
|
75
|
+
DeliveryOptions:[
|
|
76
|
+
{
|
|
77
|
+
Details:
|
|
78
|
+
{
|
|
79
|
+
"AmiDeliveryOptionDetails": {
|
|
80
|
+
"AmiSource":{
|
|
81
|
+
"AmiId": amiId,
|
|
82
|
+
"AccessRoleArn": "arn:aws:iam::590183947985:role/Theorim_MarketPlaceRole",
|
|
83
|
+
"UserName": "ec2-user",
|
|
84
|
+
"OperatingSystemName": "AMAZONLINUX",
|
|
85
|
+
"OperatingSystemVersion": "Amazon Linux 2 AMI 2.0.20220207.1 x86_64 HVM gp2"
|
|
86
|
+
},
|
|
87
|
+
"UsageInstructions": "Visit Theorim.ai/install for installation instructions",
|
|
88
|
+
"RecommendedInstanceType": "m6a.large",
|
|
89
|
+
"SecurityGroups":
|
|
90
|
+
[
|
|
91
|
+
{
|
|
92
|
+
"IpProtocol": "tcp",
|
|
93
|
+
"FromPort": 443,
|
|
94
|
+
"ToPort": 443,
|
|
95
|
+
"IpRanges":["0.0.0.0/0"]
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
"IpProtocol": "tcp",
|
|
99
|
+
"FromPort": 8080,
|
|
100
|
+
"ToPort": 8080,
|
|
101
|
+
"IpRanges":["0.0.0.0/0"]
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
]
|
|
108
|
+
}),
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
// Start the change set
|
|
114
|
+
const startChangeSetCommand = new StartChangeSetCommand(changeSet);
|
|
115
|
+
const startResponse = await client.send(startChangeSetCommand);
|
|
116
|
+
console.log("Change set started:", startResponse);
|
|
117
|
+
|
|
118
|
+
const changeSetId = startResponse.ChangeSetId;
|
|
119
|
+
|
|
120
|
+
// Poll for the status of the change set
|
|
121
|
+
let status = "IN_PROGRESS";
|
|
122
|
+
while (status === "IN_PROGRESS") {
|
|
123
|
+
await new Promise(resolve => setTimeout(resolve, 5000)); // Wait for 5 seconds before polling
|
|
124
|
+
|
|
125
|
+
const describeChangeSetCommand = new DescribeChangeSetCommand({
|
|
126
|
+
Catalog: "AWSMarketplace",
|
|
127
|
+
ChangeSetId: changeSetId,
|
|
128
|
+
});
|
|
129
|
+
const describeResponse = await client.send(describeChangeSetCommand);
|
|
130
|
+
|
|
131
|
+
status = describeResponse.Status;
|
|
132
|
+
console.log("Change set status:", status);
|
|
133
|
+
|
|
134
|
+
if (status === "SUCCEEDED") {
|
|
135
|
+
console.log("Change set succeeded:", describeResponse);
|
|
136
|
+
break;
|
|
137
|
+
} else if (status === "FAILED") {
|
|
138
|
+
console.error("Change set failed:", describeResponse);
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
} catch (error) {
|
|
143
|
+
console.error("Error updating AMI version:", error);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
// Get AMI Ids Function
|
|
150
|
+
const getRegions = async (productId) => {
|
|
151
|
+
const client = new MarketplaceCatalogClient({ region: process.env.orgRegion }); // Update region if needed
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
const command = new DescribeEntityCommand({
|
|
155
|
+
Catalog: "AWSMarketplace",
|
|
156
|
+
EntityId: productId,
|
|
157
|
+
});
|
|
158
|
+
const response = await client.send(command);
|
|
159
|
+
console.log('dd',response.DetailsDocument);
|
|
160
|
+
console.log('v',response.DetailsDocument.Versions[1].DeliveryOptions[0].AmiAlias);
|
|
161
|
+
return response.DetailsDocument.RegionAvailability.Regions;
|
|
162
|
+
// console.log("Regions:", response.DetailsDocument.Description.RegionAvailability.Regions);
|
|
163
|
+
// console.log("Version:", response.DetailsDocument.Description.RegionAvailability.Regions);
|
|
164
|
+
// Extract AMI details by region
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// List by Mktplc Listing
|
|
168
|
+
const getAMIs = async (region,productName,productCode) => {
|
|
169
|
+
const ec2Client = new EC2Client({ region });
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
const command = new DescribeImagesCommand({
|
|
173
|
+
Filters: [
|
|
174
|
+
{
|
|
175
|
+
Name: "name",
|
|
176
|
+
Values: [
|
|
177
|
+
`${productName}-*-${productCode}` // Adjust the filter to match your product naming pattern
|
|
178
|
+
]
|
|
179
|
+
}
|
|
180
|
+
]
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
const response = await ec2Client.send(command);
|
|
184
|
+
|
|
185
|
+
console.log(`Found ${response.Images.length} AMIs for product: ${productName} in region: ${region}`);
|
|
186
|
+
for (const ami of response.Images) {
|
|
187
|
+
console.log('ami',ami);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return response.Images;
|
|
191
|
+
} catch (error) {
|
|
192
|
+
console.error(`Error fetching AMIs for product: ${productName} in region: ${region}`, error);
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
|
package/main.js
CHANGED
|
@@ -19,9 +19,7 @@ const Commands = {
|
|
|
19
19
|
desc: "Update org stack",
|
|
20
20
|
exec: require('./commands/init_org').updateOrgStack,
|
|
21
21
|
args: [
|
|
22
|
-
{n: '
|
|
23
|
-
{n: 'region', desc: 'AWS Region for Core Assets. Default us-east-1', r: false},
|
|
24
|
-
{n: 'repo', desc: 'Github repo name', r: false}
|
|
22
|
+
{n: 'stack', desc: 'Updated Org Stack', r: false}
|
|
25
23
|
]
|
|
26
24
|
},
|
|
27
25
|
'set-org': {
|
|
@@ -94,6 +92,25 @@ const Commands = {
|
|
|
94
92
|
{n: 'v', desc: 'Version to launch', pattern: `[0-9]{1,20}`, r: true}
|
|
95
93
|
]
|
|
96
94
|
},
|
|
95
|
+
'new-listing': {
|
|
96
|
+
desc: 'Set a new listing for an app',
|
|
97
|
+
exec: require('./commands/publish').add_listing,
|
|
98
|
+
args: [
|
|
99
|
+
{n: 'app', desc: 'Name of existing app', pattern: `[A-Za-z]{2,20}`, r: true},
|
|
100
|
+
{n: 'pid', desc: 'Product Id', r: true}
|
|
101
|
+
]
|
|
102
|
+
},
|
|
103
|
+
'publish': {
|
|
104
|
+
desc: 'Publish app to marketplace',
|
|
105
|
+
exec: require('./commands/publish').main,
|
|
106
|
+
args: [
|
|
107
|
+
{n: 'app', desc: 'Name of existing app', pattern: `[A-Za-z]{2,20}`, r: true},
|
|
108
|
+
{n: 'desc', desc: 'Description of Changes', r: true},
|
|
109
|
+
{n: 'v', desc: 'Version to launch', pattern: `[0-9]{1,20}`, r: true},
|
|
110
|
+
{n: 'stack', desc: 'Path of stack.yaml', r: true},
|
|
111
|
+
{n: 'out', desc: 'Output path of marketplace stack', r: true}
|
|
112
|
+
]
|
|
113
|
+
},
|
|
97
114
|
'inspect': {
|
|
98
115
|
desc: 'Get stack status and Ec2 console logs for an instance',
|
|
99
116
|
exec: require('./commands/inspect').main,
|
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"name":"cloudmason","version":"1.
|
|
1
|
+
{"name":"cloudmason","version":"1.1.13","description":"","main":"main.js","scripts":{"build":"node build.js"},"bin":{"mason":"./main.js"},"repository":{"type":"git","url":"https://github.com/kai-harvey/secure-saas.git"},"author":"Kai Harvey","license":"ISC","dependencies":{"@aws-sdk/client-acm":"^3.418.0","@aws-sdk/client-auto-scaling":"^3.470.0","@aws-sdk/client-cloudformation":"^3.418.0","@aws-sdk/client-ec2":"^3.416.0","@aws-sdk/client-iam":"^3.418.0","@aws-sdk/client-marketplace-catalog":"^3.716.0","@aws-sdk/client-route-53":"^3.425.0","@aws-sdk/client-s3":"^3.418.0","@aws-sdk/client-ssm":"^3.421.0","adm-zip":"^0.5.10","yaml":"^2.6.1"}}
|