unbounce / iidy Goto Github PK
View Code? Open in Web Editor NEWiidy (Is it done yet?) -- CloudFormation with Confidence
License: MIT License
iidy (Is it done yet?) -- CloudFormation with Confidence
License: MIT License
$ iidy --profile data --region us-east-1 watch-stack verify-data-cluster-slow-test
Stack Details:
Name: verify-data-cluster-slow-test
Status CREATE_COMPLETE
Capabilities: CAPABILITY_IAM,CAPABILITY_NAMED_IAM
Service Role: None
Tags: owner=dataops, environment=test, project=ub-data-infrastructure/cluster
DisableRollback: false
Creation Time: Thu Aug 10 2017 21:21:03
Last Update Time: Thu Aug 10 2017 21:21:03
Timeout In Minutes: None
NotificationARNs: None
ARN: arn:aws:cloudformation:us-east-1:514588561872:stack/verify-data-cluster-slow-test/d0a09c60-7e11-11e7-8c6a-503acac41efd
Console URL: https://us-east-1.console.aws.amazon.com/cloudformation/home?region=us-east-1#/stack/detail?stackId=arn%3Aaws%3Acloudformation%3Aus-east-1%3A514588561872%3Astack%2Fverify-data-cluster-slow-test%2Fd0a09c60-7e11-11e7-8c6a-503acac41efd
Previous Stack Events (max 10):
Thu Aug 10 2017 21:21:11 CREATE_COMPLETE AWS::EC2::SecurityGroup EC2InstanceSecurityGroup
Thu Aug 10 2017 21:21:14 CREATE_IN_PROGRESS AWS::EC2::Instance EC2Instance
Thu Aug 10 2017 21:21:16 CREATE_IN_PROGRESS AWS::EC2::Instance EC2Instance
Thu Aug 10 2017 21:21:26 CREATE_COMPLETE AWS::EC2::Volume Volume
Thu Aug 10 2017 21:21:31 CREATE_COMPLETE AWS::S3::Bucket S3Bucket
Thu Aug 10 2017 21:21:34 CREATE_IN_PROGRESS AWS::IAM::Role IAMRole
Thu Aug 10 2017 21:21:35 CREATE_IN_PROGRESS AWS::IAM::Role IAMRole
Thu Aug 10 2017 21:21:49 CREATE_COMPLETE AWS::IAM::Role IAMRole
Thu Aug 10 2017 21:21:52 CREATE_COMPLETE AWS::EC2::Instance EC2Instance
Thu Aug 10 2017 21:21:56 CREATE_COMPLETE AWS::CloudFormation::Stack verify-data-cluster-slow-test
10 of 17 total events shown
Live Stack Events (2s poll):
⢂⠀ 58 seconds elapsed
$ cat concat.yaml
$defs:
a: [4,5,6]
foo: !$concat
- [1,2,3]
- !$ a
- [7,8,9]
$ idy render concat.yaml
foo:
- 1
- 2
- 3
- - 4
- 5
- 6
- 7
- 8
- 9
Expected:
foo:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
!GetAtt
can be provided a dotted string or an array to lookup resource attributes. The latter produces an error:
$ cat get-att.yaml
s: !GetAtt [a, b]
$ iidy render get-att.yaml
error: ref.trim is not a function
Add an optional "approval" process for changing parameter store values. This purpose of this mechanism would be to get a second set of eyes when changing values that affect production as well as allow for rollouts of parameter changes to be accepted before they are applied and affect an application's stack.
Something like:
iidy param set --with-approval /some/path/to/param value
iidy param review /some/path/to/param
iidy param review-by-path /some/path/to/
This could be implemented simply by prefixing the pending parameter's path with some key. For example, set --with-approval
would write to _pending/some/path/to/param
. param review
would pull from _pending/some/path/to/param
and /some/path/to/param
and show both to the user to approve or reject. review-by-path
would do the same, but for all pending parameters under a path.
When a changeset
is created, the stack enters REVIEW_IN_PROGRESS
.
Should iidy
include an option to immediately watch the stack until it enters a completion phase?
This isn't a difficult thing to accomplish by chaining two iidy commands. The benefit here is implicitly knowing the --stack-name argument needed by watch-stack
.
Expose what iidy has determined to be the current region (via --region
or environment variables) as a variable to be used in stack args file (similar to how environment
is exposed).
iidy should be able to run using only the AWS_* env vars.
For example, if the .aws
folder does not exist, it should not fail.
(node:1) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: ENOENT: no such file or directory, open '/root/.aws/credentials'
1) If you want to compile the package/file into executable, please pay attention to compilation warnings and specify a literal in 'require' call. 2) If you don't want to compile the package/file into executable and want to 'require' it from filesystem (likely plugin), specify an absolute path in 'require' call using process.cwd() or process.execPath.
(node:1) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
Expected no-op message and exit with status code 0, actual:
$ iidy --region us-west-2 update-stack --changeset stack-args.yaml
Stack Details:
Name: foo
Status CREATE_COMPLETE
Capabilities: CAPABILITY_IAM
...
Stack Template Diff:
info: Templates are the same
Command Metadata:
CFN Operation: CREATE_CHANGESET
Region: us-west-2
...
error: The submitted information didn't contain changes. Submit different information to create a change set. Deleting failed changeset.
If you happen to be running iidy in an environment where the credentials are not found, iidy will fail silently. In particular, I noticed this running iidy under AWS Cloud9. Running iidy render
on a template would produce no output.
Manually configuring AWS credentials with aws configure
works around the issue.
A common mistake I've seen is to misspell ServiceRoleARN
as ServiceRoleArn
(or something else). It would be helpful if iidy presented the user with a warning if unknown properties are set in a stack args file.
Many of the strings set in parameter store do not need to be secure strings, and ones that are often are echoed by default.
I think a couple changes might be nice:
--encrypt
) to upload as SecureStringiidy create-stack stack-args.yaml
Command Metadata:
CFN Operation: CREATE_STACK
Region: us-east-1
Profile: None
CLI Arguments: region=null, profile=staging, argsfile=stack-args.yaml
IAM Service Role: None
Current IAM Principal: ...
error: S3 error: The specified key does not exist.
For more information check http://docs.aws.amazon.com/AmazonS3/latest/API/ErrorResponses.html
Expecting to get a stack name missing argument, got this instead:
iidy create-stack target/infrastructure/resources.yml --profile sandbox --region us-west-2
Command Metadata:
Cloudformation operation: CREATE_STACK
Command line arguments: region=us-west-2, profile=sandbox, argsfile=target/infrastructure/resources.yml
Region: us-west-2
Profile: sandbox
Current Account / IAM Arn: 894790724410
arn:aws:sts::894790724410:assumed-role/cross-account-access-AdminCrossAccountRole-150VE9IYNKQJ4/aws-sdk-js-1501739750740
IAM Service Role: None
error: Cannot read property 'trim' of undefined
error: TypeError: Cannot read property 'trim' of undefined
at loadCFNTemplate (/snapshot/cf_templates/lib/index.js:0:0)
at stackArgsToCreateStackInput (/snapshot/cf_templates/lib/index.js:0:0)
at CreateStack._run (/snapshot/cf_templates/lib/index.js:0:0)
at CreateStack.run (/snapshot/cf_templates/lib/index.js:0:0)
Does iidy cache the credentials assumed between multiple runs (the same way the AWS CLI does?)
I'm having an issue trying to parameterize a redrive policy whithin a custom resource template. iidy is prepending the name of the resource to the parameter (DeliveryDeadLetterQueue.Arn).
iidy version: 1.6.5
# delivery-queue-template.yaml
$params:
...
- Name: RedrivePolicy
Default: !Ref 'AWS::NoValue'
...
Resources:
Queue:
Type: AWS::SQS::Queue
Properties:
...
RedrivePolicy: !$ RedrivePolicy
# cfn-template.yaml
Resources:
...
DeliveryDeadLetter:
Type: DeliveryQueue
Properties:
EventType: DeliveryDeadLetter
...
ProcessLeadEventV1:
Type: DeliveryQueue
Properties:
...
RedrivePolicy:
deadLetterTargetArn: !GetAtt DeliveryDeadLetterQueue.Arn
maxReceiveCount: 2
Expected output:
Resources:
...
ProcessLeadEventV1Queue:
Type: 'AWS::SQS::Queue'
Properties:
...
RedrivePolicy:
deadLetterTargetArn: !GetAtt DeliveryDeadLetterQueue.Arn
maxReceiveCount: 2
Current output:
Resources:
...
ProcessLeadEventV1Queue:
Type: 'AWS::SQS::Queue'
Properties:
...
RedrivePolicy:
deadLetterTargetArn: !GetAtt ProcessLeadEventV1DeliveryDeadLetterQueue.Arn
maxReceiveCount: 2
Is this the expected behavior? Is there a way to avoid renaming the parameter?
Given a template like the following:
$import:
something: my-thing-{{ other }}
where other
is never defined, no error is shown and errors can show up far from actual error site. Specifically in my case, a variable used in an ssm-path
import was never set and the error was:
error: Error in string template at Root.Parameters.Something:
"something" not defined in [object Object]
Something: {{ params.something }}
Document the base IAM permissions to do iidy {create, update, delete}-stack
etc. Optional: fail gracefully if permission are missing for "nice to have" output, such as describing the stack that was created.
Document additional permission required for imports, for example:
cfn:output:...
requires cloudformation:DescribeStack
cfn:{export, parameter, tag, resource, stack}:...
requires cloudformation:ListExports
Given a stack args like:
$imports:
params: ssm-path:/path/to/params/
...
Parameters:
ImageId: !$ params.ImageId
allow something like this to work:
iidy param set stack-args.yaml params.ImageId ami-000000
where params
is the $imports
variable name that points to a SSM parameter path. This would set /path/to/params/ImageId
to ami-000000
.
This should work with iidy param --environment ...
or other imports that are used to produce the ssm-path
.
Related, it would be nice to have iidy param update
that fails if it isn't overwriting a parameter (the opposite of iidy set
without --overwrite
). This, in conjunction with the above, would prevent users from setting the wrong parameter (eg. setting the wrong path or the correct path in the wrong region).
Feature: To re-order the keys that come out of exporting a stack and converting it to YAML.
For IAM Statements, I think the general convention is to order (in this order):
It'd be nice if iidy could normalize that on the output :D
If a variable (eg. foo
) and a nested lookup using that variable (eg. foo.bar
) are both in $defs
, the lookup can fail.
$ cat list.yaml
$defs:
a: !$split
- ','
- 1,2,3
foo: !$ a.0
$ iidy render list.yaml
foo: '1'
$ cat list.yaml
$defs:
a: !$split
- ','
- 1,2,3
foo: !$ a.0
foo: !$ foo
$ iidy render list.yaml
error: Could not find "0" at Root.foo}
It appears to be fine if the variable is a static value:
$ cat list.yaml
$defs:
a:
- 1
- 2
- 3
foo: !$ a.0
foo: !$ foo
$ iidy render list.yaml
foo: 1
Take a template, input-template.yaml
:
$imports:
Include: include.yaml
AWSTemplateFormatVersion: 2010-09-09
Resources:
SomeBucket:
Type: "Include"
Properties:
Suffix: SomeSuffix
With include.yaml
:
$params:
- Name: Suffix
Resources:
Something:
Type: "AWS:Something"
Properties:
Property1: !Sub
- "{{Prefix}}-${Foo}"
-
- Foo: !$ Suffix
Property2:
Fn::Sub:
- "{{Prefix}}-${Foo}"
-
- Foo: !$ Suffix
When input-template.yaml
is rendered, the output from Fn::Sub
and !Sub
are quite different:
Resources:
SomeBucketSomething:
Type: 'AWS:Something'
Properties:
Property1: !Sub
- '{{Prefix}}-${SomeBucketFoo}'
- - Foo: !$include Suffix
Property2:
'Fn::Sub':
- 'SomeBucket-${Foo}'
- - Foo: SomeSuffix
Fn::Sub
is correctly replacing {{Prefix}}
, and not touching ${Foo}
.
!Sub
should replace {{Prefix}}
and not touch embedded ${...}
expressions unless the match the name of an in scope variable. (Like from a $param
, $import
, or $defs
.)
Steps:
create-changeset
with a stack name that does not exist(node:77684) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): ValidationError: Stack [YOUR_STACK_NAME] does not exist
(node:77684) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
The error message demonstrated in (2) should probably be hidden since this isn't actually a failure.
In a custom resource (imported as LongLivedBucket
) I have:
Resources:
LongLivedBucket:
Type: "AWS::S3::Bucket"
...
Outputs:
Name:
Description: "The bucket's name"
Value:
!Ref LongLivedBucket
ARN:
Description: "The bucket's ARN"
Value:
!GetAtt LongLivedBucket.Arn
In the template I have:
Resources:
ConfigBucket:
Type: LongLivedBucket
This renders as:
Outputs:
ConfigBucketName:
Description: The bucket's name
Value: !Ref ConfigBucketLongLivedBucket
ConfigBucketARN:
Description: The bucket's ARN
Value: !GetAtt LongLivedBucket.Arn
The first output correctly prefixes the reference to LongLivedBucket
with ConfigBucket
, but the second output does not, which results in this template failing validation:
error: Template error: instance of Fn::GetAtt references undefined resource LongLivedBucket
Until this is fixed, a workaround is to change the !GetAtt
to the following:
!GetAtt '{{Prefix}}LongLivedBucket.Arn'
It would be great to be able to run iidy install
, it should create stack_args.yaml
in the current directory.
$PWD/template.yaml
.Tags: # aws tags to apply to the stack
owner: $(whomai)
environment: $environment || development
project: $(basename $PWD)
lifetime: short
$ cat octal.yaml
$defs:
num: '0012345'
foo: "a {{ num }} b"
$ iidy render octal.yaml
foo: a '0012345' b
Expected:
foo: a 0012345 b
Related to #69
Getting an error when !GetAtt
is throwing an error when it is passed an array:
# cfn-template.yaml
Resources:
MyCodePipeline:
Type: AWS::CodePipeline::Pipeline
Properties:
RoleArn: !GetAtt [MyRole, Arn]
error: ref.trim is not a function
error: TypeError: ref.trim is not a function
at maybeRewriteRef (/snapshot/iidy/lib/preprocess/index.js:827:30)
at Object.exports.visitGetAtt (/snapshot/iidy/lib/preprocess/index.js:835:60)
at visitYamlTagNode (/snapshot/iidy/lib/preprocess/index.js:645:24)
at result (/snapshot/iidy/lib/preprocess/index.js:882:20)
at visitNode (/snapshot/iidy/lib/preprocess/index.js:899:7)
at visitMapNode (/snapshot/iidy/lib/preprocess/index.js:969:46)
at visitPlainMap (/snapshot/iidy/lib/preprocess/index.js:947:16)
at result (/snapshot/iidy/lib/preprocess/index.js:888:20)
at visitNode (/snapshot/iidy/lib/preprocess/index.js:899:7)
at visitMapNode (/snapshot/iidy/lib/preprocess/index.js:969:46)
at visitPlainMap (/snapshot/iidy/lib/preprocess/index.js:947:16)
at result (/snapshot/iidy/lib/preprocess/index.js:888:20)
at visitNode (/snapshot/iidy/lib/preprocess/index.js:899:7)
at _.fromPairs._.map (/snapshot/iidy/lib/preprocess/index.js:576:24)
at arrayMap (/snapshot/iidy/node_modules/lodash/lodash.js:660:23)
at Function.map (/snapshot/iidy/node_modules/lodash/lodash.js:9571:14)
As a workaround, I'm using a string instead
Resources:
MyCodePipeline:
Properties:
RoleArn: !GetAtt MyRole.Arn
Currently, to describe a stack, the user has to add iidy describe-stack <stackname>
and if it's on a different region, the have to add it as --region us-west-1
. And if they are more unlucky, they will have to add a profile
which makes the command longer.
It would be great if iidy
detects the current stack_args.yaml
in the file. It should assume that in the current directory, there's only one stack_args.yaml
and template.yaml
. Convention over configuration
With this, after they have configured stack_args.yaml
, they can run commands without further configuration. iidy describe-stack
should just work, but still supporting --stackName
and other --
configuration
When you unzip the final binary from the releases page, the binary is called iidy-macos
instead of iidy
. This is confusing for new users and it makes the Homebrew configuration confusing.
Consider renaming the binary to iidy
regardless of OS/Architecture.
In iidy 1.6.0-rc5, you were able to specify Region as an environment map:
Region:
integration: us-west-2
production: us-east-1
This no longer works in iidy 1.64, instead you see this error:
error: Missing region in config
Error message from #71 is still present when doing update-stack --changeset
.
The templating import mechanism documentation in the README.md is missing the need to include render: <template>
in the stack-args.yaml
file.
Bonus points: if the tool could detect that the render is not included in the description and explicitly warn the user, that would be double-plus delightful.
Importing a value that is an octal-like string does not interpolate properly into stack args properties, for example:
$defs:
accountId: 00123456789
ServiceRoleArn: arn:aws:iam::{{ accountId }}:role/role-name
Output from iidy create-stack
:
...
IAM Service Role: arn:aws:iam::$0string 00123456789:role/role-name
...
Support rendering of multiple YAML documents within a single file, for example:
---
foo: bar
---
baz: qux
This would be very useful when using iidy rendering for non-Cloudformation YAML files.
It would be nice if, in addition to being able to use Cloudformation Service Roles, iidy could assume a role, without having to specify a profile.
Profiles require each user of iidy to have correctly configured profiles. Specifying a role arn in the stack args would make it more portable.
Create CLI command to "create or update" a stack (such as iidy apply-stack stack-args.yaml
or iidy create-or-update stack-args.yaml
).
Pre-processing the stack name would make it easier to have multiple variants of the same stack, for example having the stack name be based on the branch name - allowing per-branch releases for reviews.
Given all the features in iidy, it'd be good to have a succinct document that highlights the conventions to recommend users of iidy:
Some things I can think of:
-e
or for multi-environment setupsiidy create-stack --profile profile-with-mfa
prompts for a MFA token more then once. Entering the same token at each prompt results in the following error:
error MultiFactorAuthentication failed with invalid MFA one time pass code.
as MFA tokens are only valid for a single use.
iidy should not prompt for an MFA code more than once.
When executing delete-stack
the program hangs on polling state
Platfrom: Linux
? Are you sure you want to DELETE the stack roman-1? Yes
Live Stack Events (2s poll):
<HANGS>
Support rendering from STDIN with -
"filename":
echo 'foo: bar' | iidy render -
When using Template: render:cfn-template.yaml
, iidy.region
and iidy.environment
can not be used as variables in $imports
.
Expected AWS_ACCESS_KEY_ID
, AWS_SECRET_ACCESS_KEY
, AWS_SECURITY_TOKEN
and iidy fails at the $import step.
See this error trace:
debug: loading import: cfn:output:some-export-name
error: The security token included in the request is invalid.
error: InvalidClientTokenId: The security token included in the request is invalid.
at Request.extractError (/snapshot/iidy/node_modules/aws-sdk/lib/protocol/query.js:47:29)
at Request.callListeners (/snapshot/iidy/node_modules/aws-sdk/lib/sequential_executor.js:105:20)
at Request.emit (/snapshot/iidy/node_modules/aws-sdk/lib/sequential_executor.js:77:10)
at Request.emit (/snapshot/iidy/node_modules/aws-sdk/lib/request.js:683:14)
at Request.transition (/snapshot/iidy/node_modules/aws-sdk/lib/request.js:22:10)
at AcceptorStateMachine.runTo (/snapshot/iidy/node_modules/aws-sdk/lib/state_machine.js:14:12)
at /snapshot/iidy/node_modules/aws-sdk/lib/state_machine.js:26:10
at Request.<anonymous> (/snapshot/iidy/node_modules/aws-sdk/lib/request.js:38:9)
at Request.<anonymous> (/snapshot/iidy/node_modules/aws-sdk/lib/request.js:685:12)
at Request.callListeners (/snapshot/iidy/node_modules/aws-sdk/lib/sequential_executor.js:115:18)
at Request.emit (/snapshot/iidy/node_modules/aws-sdk/lib/sequential_executor.js:77:10)
at Request.emit (/snapshot/iidy/node_modules/aws-sdk/lib/request.js:683:14)
at Request.transition (/snapshot/iidy/node_modules/aws-sdk/lib/request.js:22:10)
at AcceptorStateMachine.runTo (/snapshot/iidy/node_modules/aws-sdk/lib/state_machine.js:14:12)
at /snapshot/iidy/node_modules/aws-sdk/lib/state_machine.js:26:10
at Request.<anonymous> (/snapshot/iidy/node_modules/aws-sdk/lib/request.js:38:9)
make: *** [launch-stack] Error 1
Expected: stack created in us-west-2
Actual: stack created in us-east-1
$ env | grep AWS
$ cat stack-args.yaml
$imports:
stackLabel: env:STACK_LABEL:slim
Region: us-west-2
StackName: 'my-stack-{{ stackLabel }}'
Template: cfn-template.yaml
Parameters:
...
$ iidy --debug create-stack stack-args.yaml
debug: argsdata -> stackArgs { '$imports': { stackLabel: 'env:STACK_LABEL:slim' },
Region: 'us-west-2',
StackName: 'my-stack-{{ stackLabel }}',
Template: 'cfn-template.yaml',
Parameters:
{ ... },
'$envValues':
{ region: 'us-east-1',
environment: 'development',
iidy: { environment: 'development', region: 'us-east-1' },
stackLabel: 'slim' } }
...
Command Metadata:
CFN Operation: CREATE_STACK
Region: us-west-2
CLI Arguments: region=null, profile=null, argsfile=infrastructure/ssh_security_group/stack-args.yaml
...
ARN: arn:aws:cloudformation:us-east-1:...:stack/...
Console URL: https://us-west-2.console.aws.amazon.com/cloudformation/home?region=us-west-2#/stack/detail?stackId=...
iidy should make use of (and contribute to) AWS CLI's assumed credentials cache.
Possible implementation: aws/aws-sdk-js#993 (comment)
The values region
and profile
should be carried through to the exec-changeset
command.
The current output looks like this:
Your new stack is now in REVIEW_IN_PROGRESS state. To create the resources run the following
iidy exec-changeset --stack-name your-stack iam/stack-args.yml sturdy-rat
It would be preferrable pass through the region and profile of the previous command if possible:
iidy --region REGION --profile PROFILE exec-changeset --stack-name your-stack iam/stack-args.yml sturdy-rat
iidy does not support the functionality of MFA requirements on role assumptions (--profile
in AWS CLI parlance). This is used to enforce role assumptions constraints when working with escalated IAM role privileges or working in a multi-account structure.
iidy ... --profile other-account
will return an STS assume role error.
Leverage parameter store history to debug and resolve issues due to changes in parameter store values.
Running existing commands, with parameters as they were at a given point in time:
iidy create-stack --point-in-time '2018-01-01 3:30' stack-args.yaml
iidy update-stack --point-in-time '2018-01-01 3:30' stack-args.yaml
iidy param get-by-path --point-in-time '2018-01-01 3:30' /some/path
Viewing how values have changed:
# Show changes since date
iidy param diff /some/path 2018-01-01
# Show changes between dates
iidy param diff /some/path '2018-01-01 3:30' '2018-01-01 4:30'
Add the time it took to create or update each resource on it's CREATE_COMPLETE/UPDATE_COMPLETE line (maybe delete and rollback too?). For example (times added in parentheses at the end of the line):
Wed Oct 24 2018 21:30:30 CREATE_IN_PROGRESS AWS::CloudFormation::Stack my-stack
Wed Oct 24 2018 21:30:44 CREATE_IN_PROGRESS AWS::IAM::Role MyRole
Wed Oct 24 2018 21:30:44 CREATE_IN_PROGRESS AWS::IAM::Role MyRole
Wed Oct 24 2018 21:30:57 CREATE_IN_PROGRESS AWS::EKS::Cluster EKSCluster
Wed Oct 24 2018 21:31:01 CREATE_IN_PROGRESS AWS::EKS::Cluster EKSCluster
Wed Oct 24 2018 21:31:04 CREATE_COMPLETE AWS::IAM::Role MyRole (20s)
Wed Oct 24 2018 21:40:48 CREATE_COMPLETE AWS::EKS::Cluster EKSCluster (9m 51s)
Wed Oct 24 2018 21:40:54 CREATE_COMPLETE AWS::CloudFormation::Stack my-stack (10m 24s)
This will aid the identification of slow-to-create resources in a CloudFormation stack.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.