├── cfn-init
├── index.htm
└── ec2-with-cfn-init.yml
├── .gitignore
├── wait_conditions
├── curl_command_send_signal.sh
├── wait_signal_payload.json
└── wait_condition_example.yml
├── .github
├── CODEOWNERS
├── PULL_REQUEST_TEMPLATE.md
├── workflows
│ └── main.yml
└── ISSUE_TEMPLATE.md
├── nested_stacks
├── package_command.sh
├── compute.yml
├── security.yml
└── root_stack.yml
├── modules
├── webstack-module
│ ├── .rpdk-config
│ ├── fragments
│ │ └── sample.yml
│ └── schema.json
└── use_module.yml
├── static-website
├── index.html
├── basic_s3.yml
└── website.yml
├── template.yaml
├── cli
├── command.sh
└── basic_s3.yml
├── macros
├── macro_usage_template_level.yml
├── macro_definition_stack.yml
├── macro_usage_with_parameters.yml
├── index.mjs
└── transform.mjs
├── basic_s3.yml
├── export_import
├── input_value_subnet.yml
└── export_value_vpc.yml
├── dynamic_references
└── resolve-parameter-simple.yml
├── stack_sets
├── simple_vpc.yml
├── AWSCloudFormationStackSetExecutionRole.yml
└── AWSCloudFormationStackSetAdministrationRole.yml
├── CONTRIBUTING.md
├── NOTICE
├── custom_resources
├── use-custom-resource.yml
└── custom-resource-standalone.yml
├── developer
└── lint-problems.yml
├── instance_provisioning
└── creation_policy.yml
├── automation
├── template.yml
├── v2
│ └── pipeline.yml
└── v1
│ └── pipeline.yml
├── basic_ec2
├── change_set_instance.yml
└── ec2_nginx.yml
├── README.md
├── conditions
└── conditions.yml
├── parameters
└── all_parameter_types.yml
└── LICENSE
/cfn-init/index.htm:
--------------------------------------------------------------------------------
1 |
Hello World!
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | .tmp
4 | npm-debug.log
5 |
--------------------------------------------------------------------------------
/wait_conditions/curl_command_send_signal.sh:
--------------------------------------------------------------------------------
1 | curl -T ./wait_signal_payload.json "URL_HERE"
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # Codeowners for these exercise files:
2 | # * (asterisk) denotes "all files and folders"
3 | # Example: * @producer @instructor
4 |
--------------------------------------------------------------------------------
/nested_stacks/package_command.sh:
--------------------------------------------------------------------------------
1 | aws cloudformation package --template-file root_stack.yml --s3-bucket --output-template-file output.yml
2 |
--------------------------------------------------------------------------------
/modules/webstack-module/.rpdk-config:
--------------------------------------------------------------------------------
1 | {
2 | "artifact_type": "MODULE",
3 | "typeName": "LinkedIn::Learning::WebStack::MODULE",
4 | "settings": {}
5 | }
6 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/wait_conditions/wait_signal_payload.json:
--------------------------------------------------------------------------------
1 | {
2 | "Status" : "SUCCESS",
3 | "UniqueId" : "20001",
4 | "Data" : "Hello World",
5 | "Reason" : "No Reason Provided"
6 | }
--------------------------------------------------------------------------------
/static-website/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Static Site Deployed Using CloudFormation
4 | Hello! This site was deployed using CloudFormation. Go cloud!
5 |
6 |
--------------------------------------------------------------------------------
/template.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | AWSTemplateFormatVersion: '2010-09-09'
3 | Resources:
4 | VPC:
5 | Type: AWS::EC2::VPC
6 | Properties:
7 | CidrBlock: "10.0.0.0/16"
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/static-website/basic_s3.yml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: 2010-09-09
2 | Description: A simple CloudFormation template
3 | Resources:
4 | Bucket:
5 | Type: AWS::S3::Bucket
6 | Properties:
7 | BucketName: my-cloudformation-demo
--------------------------------------------------------------------------------
/cli/command.sh:
--------------------------------------------------------------------------------
1 | aws cloudformation deploy --template-file ./basic_s3.yml --stack-name cli-test-bucket --parameter-overrides BucketName=
2 |
3 | aws cloudformation list-stacks
4 |
5 | aws s3 ls
6 |
7 | aws cloudformation delete-stack --stack-name cli-test-bucket
8 |
9 | aws cloudformation describe-stack-events --stack-name cli-test-bucket
10 |
--------------------------------------------------------------------------------
/macros/macro_usage_template_level.yml:
--------------------------------------------------------------------------------
1 | ---
2 | AWSTemplateFormatVersion: 2010-09-09
3 | Description: |
4 | A template that demonstrates how to use a macro at the template level.
5 | Parameters:
6 | BucketName:
7 | Type: String
8 | Transform: [MyNoChangeMacro]
9 | Resources:
10 | Bucket:
11 | Type: AWS::S3::Bucket
12 | Properties:
13 | BucketName: !Ref BucketName
--------------------------------------------------------------------------------
/basic_s3.yml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: 2010-09-09
2 | Description: A simple CloudFormation template
3 | Parameters:
4 | BucketName:
5 | Description: |
6 | Provide a bucket name to hold packaged resources
7 | Type: String
8 | Resources:
9 | Bucket:
10 | Type: AWS::S3::Bucket
11 | Properties:
12 | BucketName: !Ref BucketName
13 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: Copy To Branches
2 | on:
3 | workflow_dispatch:
4 | jobs:
5 | copy-to-branches:
6 | runs-on: ubuntu-latest
7 | steps:
8 | - uses: actions/checkout@v2
9 | with:
10 | fetch-depth: 0
11 | - name: Copy To Branches Action
12 | uses: planetoftheweb/copy-to-branches@v1.2
13 | env:
14 | key: main
15 |
--------------------------------------------------------------------------------
/cli/basic_s3.yml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: 2010-09-09
2 | Description: A simple CloudFormation template
3 | Parameters:
4 | BucketName:
5 | Description: |
6 | Provide a bucket name to hold packaged resources
7 | Type: String
8 | Resources:
9 | Bucket:
10 | Type: AWS::S3::Bucket
11 | Properties:
12 | BucketName: !Ref BucketName
13 |
--------------------------------------------------------------------------------
/export_import/input_value_subnet.yml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: 2010-09-09
2 | Description: Create a subnet that depends on a vpc in another stack.
3 | Resources:
4 | MySubnet:
5 | Type: AWS::EC2::Subnet
6 | Properties:
7 | AvailabilityZone: us-east-1b
8 | CidrBlock: 10.0.0.0/24
9 | VpcId: !ImportValue ExportedVPCID
10 | Tags:
11 | - Key: Name
12 | Value: DeleteThisSubnet
--------------------------------------------------------------------------------
/dynamic_references/resolve-parameter-simple.yml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: 2010-09-09
2 | Description: A simple CloudFormation template
3 | Resources:
4 | MyBucket:
5 | Type: AWS::S3::Bucket
6 | Properties:
7 | BucketName: '{{resolve:ssm:MyBucketNameParameter}}'
8 | MyBucket2:
9 | Type: AWS::S3::Bucket
10 | Properties:
11 | BucketName: '{{resolve:secretsmanager:MyBucketSecret2:SecretString:bucketname}}'
--------------------------------------------------------------------------------
/macros/macro_definition_stack.yml:
--------------------------------------------------------------------------------
1 | ---
2 | AWSTemplateFormatVersion: '2010-09-09'
3 | Description: |
4 | A template that creates a macro definition based on the pre-existing lambda ARN.
5 | Parameters:
6 | FunctionARN:
7 | Description: ARN of the macro lambda function
8 | Type: String
9 | Resources:
10 | Macro:
11 | Type: AWS::CloudFormation::Macro
12 | Properties:
13 | Name: MyNoChangeMacro
14 | Description: Definition for the macro
15 | FunctionName: !Ref FunctionARN
--------------------------------------------------------------------------------
/stack_sets/simple_vpc.yml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: 2010-09-09
2 | Description: Simple VPC creation
3 | Resources:
4 | MyVPC:
5 | Type: AWS::EC2::VPC
6 | Properties:
7 | CidrBlock: 10.0.0.0/16
8 | EnableDnsHostnames: true
9 | EnableDnsSupport: true
10 | InstanceTenancy: default
11 | Tags:
12 | - Key: Name
13 | Value: DeleteThisVPC
14 | Outputs:
15 | VPCID:
16 | Description: the ID of this VPC
17 | Value: !Ref MyVPC
18 | Export:
19 | Name: ExportedVPCID
--------------------------------------------------------------------------------
/static-website/website.yml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: 2010-09-09
2 | Description: A simple CloudFormation template
3 | Resources:
4 | Bucket:
5 | Type: AWS::S3::Bucket
6 | Properties:
7 | BucketName: my-cloudformation-demo
8 | AccessControl: PublicRead
9 | WebsiteConfiguration:
10 | IndexDocument: index.html
11 | Outputs:
12 | WebsiteURL:
13 | Value: !GetAtt [Bucket, WebsiteURL]
14 | Description: URL for website hosted on S3
--------------------------------------------------------------------------------
/export_import/export_value_vpc.yml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: 2010-09-09
2 | Description: Simple VPC creation
3 | Resources:
4 | MyVPC:
5 | Type: AWS::EC2::VPC
6 | Properties:
7 | CidrBlock: 10.0.0.0/16
8 | EnableDnsHostnames: true
9 | EnableDnsSupport: true
10 | InstanceTenancy: default
11 | Tags:
12 | - Key: Name
13 | Value: DeleteThisVPC
14 | Outputs:
15 | VPCID:
16 | Description: the ID of this VPC
17 | Value: !Ref MyVPC
18 | Export:
19 | Name: ExportedVPCID
--------------------------------------------------------------------------------
/modules/use_module.yml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: 2010-09-09
2 | Description: |
3 |
4 | Parameters:
5 | Subnet:
6 | Description: Subnet in which to place the resources
7 | Type: AWS::EC2::Subnet::Id
8 | # this type is not that helpful, since it just shows IDs
9 | Resources:
10 | WebStackResource:
11 | Type: Brandon::Demo::WebStack::MODULE
12 | Properties:
13 | Subnet: !Ref Subnet
14 | InstanceType: t3.micro
15 | Outputs:
16 | IPAddress:
17 | Description: Fetched from module outputs
18 | Value: WebStackResource.Outputs.InstanceIP
--------------------------------------------------------------------------------
/macros/macro_usage_with_parameters.yml:
--------------------------------------------------------------------------------
1 | ---
2 | AWSTemplateFormatVersion: 2010-09-09
3 | Description: |
4 | A template that demonstrates how to use a macro at both
5 | template and snippet levels with parameters.
6 | Parameters:
7 | BucketName:
8 | Type: String
9 | Transform:
10 | - Name: MyNoChangeMacro
11 | Parameters:
12 | Hello: World
13 | Resources:
14 | Bucket:
15 | Type: AWS::S3::Bucket
16 | Properties:
17 | BucketName: !Ref BucketName
18 | 'Fn::Transform': # cfn lint incorrectly flags this
19 | Name: 'MyNoChangeMacro'
20 | Parameters:
21 | Foo: Bar
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 |
2 | Contribution Agreement
3 | ======================
4 |
5 | This repository does not accept pull requests (PRs). All pull requests will be closed.
6 |
7 | However, if any contributions (through pull requests, issues, feedback or otherwise) are provided, as a contributor, you represent that the code you submit is your original work or that of your employer (in which case you represent you have the right to bind your employer). By submitting code (or otherwise providing feedback), you (and, if applicable, your employer) are licensing the submitted code (and/or feedback) to LinkedIn and the open source community subject to the BSD 2-Clause license.
8 |
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | Copyright 2023 LinkedIn Corporation
2 | All Rights Reserved.
3 |
4 | Licensed under the LinkedIn Learning Exercise File License (the "License").
5 | See LICENSE in the project root for license information.
6 |
7 | Please note, this project may automatically load third party code from external
8 | repositories (for example, NPM modules, Composer packages, or other dependencies).
9 | If so, such third party code may be subject to other license terms than as set
10 | forth above. In addition, such third party code may also depend on and load
11 | multiple tiers of dependencies. Please review the applicable licenses of the
12 | additional dependencies.
13 |
--------------------------------------------------------------------------------
/macros/index.mjs:
--------------------------------------------------------------------------------
1 | export const handler = async(event) => {
2 |
3 | // var region = event.region
4 | // var accountID = event.accountID
5 | // var fragment = event.fragment
6 | // var transformId = event.transformId
7 | // var params = event.params
8 | // var requestID = event.requestId
9 | // var templateParameterValues = event.templateParameterValues
10 |
11 | console.log("Hello from lambda")
12 | console.log( "event params: " + JSON.stringify(event, 2, null))
13 |
14 | const response = {
15 | requestId: event.requestId,
16 | status: "success",
17 | fragment: event.fragment // no change
18 | };
19 | return response;
20 | };
21 |
--------------------------------------------------------------------------------
/wait_conditions/wait_condition_example.yml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: 2010-09-09
2 | Description: |
3 | Shows a wait condition
4 | Resources:
5 | MyBucket:
6 | Type: AWS::S3::Bucket
7 | Properties:
8 | BucketName: bmr-some-bucket
9 | MyWaitCondition:
10 | Type: AWS::CloudFormation::WaitCondition
11 | Properties:
12 | Count: 1
13 | Handle: !Ref MyWaitHandle
14 | Timeout: 300 # 5 minutes
15 | DependsOn: MyBucket
16 | MyWaitHandle:
17 | Type: AWS::CloudFormation::WaitConditionHandle # we should see the URL in this event output.
18 | Outputs:
19 | MySignalData:
20 | Description: Data returned from whatever signaled the handler.
21 | Value: !GetAtt MyWaitCondition.Data
--------------------------------------------------------------------------------
/nested_stacks/compute.yml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: 2010-09-09
2 | Description: |
3 | Creates an EC2 instance
4 |
5 | Parameters:
6 | SecurityGroupId:
7 | Description: A security group to apply to the instance
8 | Type: String
9 |
10 | Resources:
11 | MyInstance:
12 | Type: AWS::EC2::Instance
13 | Properties:
14 | UserData:
15 | Fn::Base64: |
16 | #!/bin/bash -xe
17 | sudo amazon-linux-extras install -y nginx1
18 | sudo service nginx start
19 | ImageId: ami-0b0dcb5067f052a63
20 | InstanceType: t3.micro
21 | SecurityGroupIds:
22 | - !Ref SecurityGroupId
23 | Tags:
24 | - Key: Name
25 | Value: MyNestedNginxInstance
26 | Outputs:
27 | InstanceIp:
28 | Value: !GetAtt MyInstance.PublicIp
--------------------------------------------------------------------------------
/custom_resources/use-custom-resource.yml:
--------------------------------------------------------------------------------
1 | Description: >
2 | Using a custom resource by referring to an exported Arn.
3 | Custom resource is created in custom-resource-standalone.yml
4 | Parameters:
5 |
6 | BucketName:
7 | Type: String
8 | Description: The name of the s3 bucket you want to create
9 |
10 | Resources:
11 | MyCustomResourceCallout:
12 | Type: Custom::LambdaCallout
13 | Properties:
14 | ServiceToken: !ImportValue MyCustomResourceArn
15 | BucketName: !Ref BucketName
16 |
17 | Outputs:
18 | OutputFromFunction:
19 | Description: Output from the custom function
20 | Value: !GetAtt MyCustomResourceCallout.msg
21 |
22 | ResponseText:
23 | Description: Output from the custom function
24 | Value: !GetAtt MyCustomResourceCallout.responseText
--------------------------------------------------------------------------------
/nested_stacks/security.yml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: 2010-09-09
2 | Description: |
3 | Standalone security group
4 |
5 | Parameters:
6 | OpenPort:
7 | Description: HTTP Port to open
8 | Type: String
9 |
10 | Resources:
11 | SecurityGroupHTTPFromWorld:
12 | Type: AWS::EC2::SecurityGroup
13 | Properties:
14 | GroupDescription: Allows port 80 from the world
15 | GroupName: !Sub 'HTTPFromAnywhere-${AWS::StackId}'
16 |
17 | # If we do not specify egress, the default
18 | # "all traffic" egress rule will be created.
19 | SecurityGroupIngress:
20 | - IpProtocol: tcp
21 | FromPort: !Ref OpenPort
22 | ToPort: !Ref OpenPort
23 | CidrIp: 0.0.0.0/0
24 |
25 | Outputs:
26 | SecurityGroupId:
27 | Value: !GetAtt SecurityGroupHTTPFromWorld.GroupId
--------------------------------------------------------------------------------
/stack_sets/AWSCloudFormationStackSetExecutionRole.yml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: 2010-09-09
2 | Description: Configure the AWSCloudFormationStackSetExecutionRole to enable use of your account as a target account in AWS CloudFormation StackSets.
3 |
4 | Parameters:
5 | AdministratorAccountId:
6 | Type: String
7 | Description: AWS Account Id of the administrator account (the account in which StackSets will be created).
8 | MaxLength: 12
9 | MinLength: 12
10 |
11 | Resources:
12 | ExecutionRole:
13 | Type: AWS::IAM::Role
14 | Properties:
15 | RoleName: AWSCloudFormationStackSetExecutionRole
16 | AssumeRolePolicyDocument:
17 | Version: 2012-10-17
18 | Statement:
19 | - Effect: Allow
20 | Principal:
21 | AWS:
22 | - !Ref AdministratorAccountId
23 | Action:
24 | - sts:AssumeRole
25 | Path: /
26 | ManagedPolicyArns:
27 | - !Sub arn:${AWS::Partition}:iam::aws:policy/AdministratorAccess
28 |
--------------------------------------------------------------------------------
/nested_stacks/root_stack.yml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: 2010-09-09
2 | Description: |
3 | Demonstration of nested stacks
4 |
5 | Parameters:
6 | OpenPort:
7 | Description: Open HTTP on this port
8 | Type: String
9 |
10 | Resources:
11 | SecurityStack:
12 | Type: AWS::CloudFormation::Stack
13 | Properties:
14 | Parameters:
15 | OpenPort: !Ref OpenPort
16 | TemplateURL: ./security.yml
17 |
18 | ComputeStack:
19 | Type: AWS::CloudFormation::Stack
20 | Properties:
21 | Parameters:
22 | # using different variables names to show they don't need to match
23 | # This usage does NOT rely on export/ImportValue
24 | # This parameter must be defined in the compute template, just as if
25 | # it were intended to be user-defined.
26 | SecurityGroupId: !GetAtt SecurityStack.Outputs.SecurityGroupId
27 | TemplateURL: ./compute.yml
28 |
29 | Outputs:
30 | InstanceIP:
31 | Description: Get to nginx here!
32 | Value: !GetAtt ComputeStack.Outputs.InstanceIp
--------------------------------------------------------------------------------
/stack_sets/AWSCloudFormationStackSetAdministrationRole.yml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: 2010-09-09
2 | Description: Configure the AWSCloudFormationStackSetAdministrationRole to enable use of AWS CloudFormation StackSets.
3 |
4 | Resources:
5 | AdministrationRole:
6 | Type: AWS::IAM::Role
7 | Properties:
8 | RoleName: AWSCloudFormationStackSetAdministrationRole
9 | AssumeRolePolicyDocument:
10 | Version: 2012-10-17
11 | Statement:
12 | - Effect: Allow
13 | Principal:
14 | Service: cloudformation.amazonaws.com
15 | Action:
16 | - sts:AssumeRole
17 | Path: /
18 | Policies:
19 | - PolicyName: AssumeRole-AWSCloudFormationStackSetExecutionRole
20 | PolicyDocument:
21 | Version: 2012-10-17
22 | Statement:
23 | - Effect: Allow
24 | Action:
25 | - sts:AssumeRole
26 | Resource:
27 | - "arn:*:iam::*:role/AWSCloudFormationStackSetExecutionRole"
28 |
--------------------------------------------------------------------------------
/developer/lint-problems.yml:
--------------------------------------------------------------------------------
1 | ---
2 | AWSTemplateFormatVersion: '2010-09-09'
3 | Descrition: A test stack for demonstrating Cloudformation
4 | Parameters:
5 | Subnet:
6 | Description: Subnet in which to place the resources
7 | Type: AWS::EC2::Subnet::Id
8 | SecurityGroup:
9 | Description: This group will be applied to both the ELB and instances in the ASG
10 | Type: AWS::EC2::SecurityGroup::I
11 | InstanceType:
12 | Description: The instance type for the Launch Config
13 | Type: String
14 | Default: t2.micro
15 | AllowedValues:
16 | - t2.nano
17 | - t2.micro
18 | - t2.small
19 | - t2.medium
20 | - t2.large
21 | Resources:
22 | My_instance:
23 | Type: AWS::EC2::Instance
24 | Properties:
25 | UserData:
26 | Fn::Base64: !Sub |
27 | #!/bin/bash -xe
28 | yum install nginx -y
29 | sudo service nginx start
30 | ImageId: ami-087c17d1fe0178315
31 | InstanceType:
32 | Ref: InstanceType
33 | SecurityGroupIds:
34 | - Ref: SecurityGroup
35 | SubnetId:
36 | Ref: Subnet
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
7 |
8 | ## Issue Overview
9 |
10 |
11 | ## Describe your environment
12 |
13 |
14 | ## Steps to Reproduce
15 |
16 | 1.
17 | 2.
18 | 3.
19 | 4.
20 |
21 | ## Expected Behavior
22 |
23 |
24 | ## Current Behavior
25 |
26 |
27 | ## Possible Solution
28 |
29 |
30 | ## Screenshots / Video
31 |
32 |
33 | ## Related Issues
34 |
35 |
--------------------------------------------------------------------------------
/instance_provisioning/creation_policy.yml:
--------------------------------------------------------------------------------
1 | ---
2 | AWSTemplateFormatVersion: '2010-09-09'
3 | Description: A test stack for demonstrating Cloudformation
4 | Parameters:
5 | Subnet:
6 | Description: Subnet in which to place the resources
7 | Type: AWS::EC2::Subnet::Id
8 | InstanceType:
9 | Description: The instance type for the Launch Config
10 | Type: String
11 | Default: t2.micro
12 | AllowedValues:
13 | - t2.nano
14 | - t2.micro
15 | - t2.small
16 | - t2.medium
17 | - t2.large
18 | Resources:
19 | MyInstance:
20 | Type: AWS::EC2::Instance
21 | Properties:
22 | UserData:
23 | Fn::Base64: !Sub |
24 | #!/bin/bash -xe
25 | sudo amazon-linux-extras install -y nginx1
26 | sudo service nginx start
27 | /opt/aws/bin/cfn-signal -exit-code 0 --stack ${AWS::StackName} --resource MyInstance --region ${AWS::Region}
28 | CreationPolicy: # note the nesting. CreationPolicy is not under Properties, but a direct child of the resource.
29 | ResourceSignal:
30 | Timeout: PT15M
31 | Count: 5
32 | ImageId: ami-087c17d1fe0178315
33 | InstanceType:
34 | Ref: InstanceType
35 | SubnetId:
36 | Ref: Subnet
--------------------------------------------------------------------------------
/automation/template.yml:
--------------------------------------------------------------------------------
1 | ---
2 | AWSTemplateFormatVersion: '2010-09-09'
3 | Description: A test stack for demonstrating Cloudformation
4 | Resources:
5 | SecurityGroupHTTPFromWorld:
6 | Type: AWS::EC2::SecurityGroup
7 | Properties:
8 | GroupDescription: Allows port 80 from the world
9 | GroupName: HTTPFromWorld
10 | # If we do not specify egress, the default
11 | # "all traffic" egress rule will be created.
12 | SecurityGroupIngress:
13 | - IpProtocol: tcp
14 | FromPort: 80
15 | ToPort: 80
16 | CidrIp: 0.0.0.0/0
17 | # Do I need VPC ID?
18 | Tags:
19 | - Key: Name
20 | Value: HTTPFromWorld
21 | MyInstance:
22 | Type: AWS::EC2::Instance
23 | Properties:
24 | UserData:
25 | Fn::Base64: |
26 | #!/bin/bash -xe
27 | sudo amazon-linux-extras install -y nginx1
28 | sudo service nginx start
29 | ImageId: ami-087c17d1fe0178315
30 | InstanceType: t3.small
31 | SecurityGroupIds:
32 | - !GetAtt SecurityGroupHTTPFromWorld.GroupId
33 | Tags:
34 | - Key: Name
35 | Value: MyNginxInstance
36 | Outputs:
37 | InstanceIP:
38 | Description: The public IP of the instance
39 | Value: !GetAtt MyInstance.PublicIp
--------------------------------------------------------------------------------
/macros/transform.mjs:
--------------------------------------------------------------------------------
1 | export const handler = async(event) => {
2 |
3 | // var region = event.region
4 | // var accountID = event.accountID
5 | // var fragment = event.fragment
6 | // var transformId = event.transformId
7 | // var params = event.params
8 | // var requestID = event.requestId
9 | // var templateParameterValues = event.templateParameterValues
10 |
11 | console.log("Hello from lambda")
12 | console.log( "event params: " + JSON.stringify(event, 2, null))
13 |
14 |
15 | // START: TRANSFORM THE JSON
16 | var tags = [
17 | { "Key" : "Created by",
18 | "Value" : "me"
19 | },
20 | { "Key" : "Created on",
21 | "Value" : new Date().toISOString()
22 | }
23 | ]
24 | // event.fragment is already parsed into JSON
25 | if ( event.fragment.hasOwnProperty("Resources") ) {
26 | var resources = event.fragment["Resources"]
27 | Object.keys( resources ).forEach( (resourceLabel) => {
28 | console.log("Encountered resource " + resourceLabel )
29 | resources[resourceLabel]["Properties"]["Tags"] = tags // note: this will replace any existing tags
30 | })
31 | }
32 | console.log("Converted fragment: " + JSON.stringify(event.fragment, 2, null))
33 | // END: TRANSFORM THE JSON
34 |
35 | const response = {
36 | requestId: event.requestId,
37 | status: "success",
38 | fragment: event.fragment // no change
39 | };
40 | return response;
41 | };
42 |
--------------------------------------------------------------------------------
/modules/webstack-module/fragments/sample.yml:
--------------------------------------------------------------------------------
1 | ---
2 | AWSTemplateFormatVersion: '2010-09-09'
3 | Description: A test stack for demonstrating Cloudformation
4 | Parameters:
5 | Subnet:
6 | Description: Subnet in which to place the resources
7 | Type: AWS::EC2::Subnet::Id
8 | # this type is not that helpful, since it just shows IDs
9 | InstanceType:
10 | Description: The instance type for the Launch Config
11 | Type: String
12 | Default: t3.micro
13 | AllowedValues:
14 | - t3.nano
15 | - t3.micro
16 | - t3.small
17 | - t3.medium
18 | - t3.large
19 | Resources:
20 | SecurityGroupHTTPFromWorld:
21 | Type: AWS::EC2::SecurityGroup
22 | Properties:
23 | GroupDescription: Allows port 80 from the world
24 | GroupName: !Sub 'HTTPFromWorld-${AWS::StackName}'
25 | # If we do not specify egress, the default
26 | # "all traffic" egress rule will be created.
27 | SecurityGroupIngress:
28 | - IpProtocol: tcp
29 | FromPort: 80
30 | ToPort: 80
31 | CidrIp: 0.0.0.0/0
32 | # Do I need VPC ID?
33 | Tags:
34 | - Key: Name
35 | Value: HTTPFromWorld
36 | MyInstance:
37 | Type: AWS::EC2::Instance
38 | Properties:
39 | UserData:
40 | Fn::Base64: |
41 | #!/bin/bash -xe
42 | sudo amazon-linux-extras install -y nginx1
43 | sudo service nginx start
44 | ImageId: ami-0b0dcb5067f052a63
45 | InstanceType:
46 | Ref: InstanceType
47 | SecurityGroupIds:
48 | - !GetAtt SecurityGroupHTTPFromWorld.GroupId
49 | SubnetId:
50 | Ref: Subnet
51 | Tags:
52 | - Key: Name
53 | Value: MyNginxInstance
54 | Outputs:
55 | InstanceIP:
56 | Description: The public IP of the instance
57 | Value: !GetAtt MyInstance.PublicIp
--------------------------------------------------------------------------------
/basic_ec2/change_set_instance.yml:
--------------------------------------------------------------------------------
1 | ---
2 | AWSTemplateFormatVersion: '2010-09-09'
3 | Description: A test stack for demonstrating Cloudformation
4 | Parameters:
5 | Subnet:
6 | Description: Subnet in which to place the resources
7 | Type: AWS::EC2::Subnet::Id
8 | # this type is not that helpful, since it just shows IDs
9 | InstanceType:
10 | Description: The instance type for the Launch Config
11 | Type: String
12 | Default: t2.micro
13 | AllowedValues:
14 | - t2.nano
15 | - t2.micro
16 | - t2.small
17 | - t2.medium
18 | - t2.large
19 | AMI:
20 | Description: a reference to the AMI you want
21 | Type: AWS::EC2::Image::Id
22 | Default: ami-0b0dcb5067f052a63
23 | InstanceName:
24 | Description: The Name tag that will get applied to the instance
25 | Type: String
26 | Default: NginxDemo
27 | Resources:
28 | SecurityGroupHTTPFromWorld:
29 | Type: AWS::EC2::SecurityGroup
30 | Properties:
31 | GroupDescription: Allows port 80 from the world
32 | GroupName: HTTPFromWorld
33 | # If we do not specify egress, the default
34 | # "all traffic" egress rule will be created.
35 | SecurityGroupIngress:
36 | - IpProtocol: tcp
37 | FromPort: 22
38 | ToPort: 22
39 | CidrIp: 0.0.0.0/0
40 | # Do I need VPC ID?
41 | MyInstance:
42 | Type: AWS::EC2::Instance
43 | Properties:
44 | UserData:
45 | Fn::Base64: |
46 | #!/bin/bash -xe
47 | sudo amazon-linux-extras install -y nginx1
48 | sudo service nginx start
49 | ImageId: !Ref AMI
50 | InstanceType:
51 | Ref: InstanceType
52 | SecurityGroupIds:
53 | - !GetAtt SecurityGroupHTTPFromWorld.GroupId
54 | SubnetId:
55 | Ref: Subnet
56 | Tags:
57 | - Key: Name
58 | Value: !Ref InstanceName
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Advanced AWS CloudFormation for Enterprise
2 | This is the repository for the LinkedIn Learning course Advanced AWS CloudFormation for Enterprise. The full course is available from [LinkedIn Learning][lil-course-url].
3 |
4 | ![Advanced AWS CloudFormation for Enterprise][lil-thumbnail-url]
5 |
6 | With AWS CloudFormation, DevOps professionals are able to create a collection of AWS and third-party resources, then easily provision, manage, and automate them. Join IT architect and Notre Dame professor Brandon Rich as he covers the skills you need to know to get the most out of AWS CloudFormation for enterprise.
7 |
8 | Expand your technical know-how as a DevOps pro and AWS system administrator, exploring built-in functions, the AWS command line, custom resources, composable and reusable templates, CloudFormation macros, and more. By the end of this course, you’ll be ready to not only provision and manage your own resource collections, but to successfully automate them as well.
9 |
10 |
11 |
12 | ## Instructions
13 | - For most of the course content, you'll be fine to just clone this repository and work from a local copy.
14 | - However, if you want to make changes or if you would like to follow along with the later video on automating templates via Code Pipeline, you'll need to fork the repo into your own account. This will give you a copy that you can treat as your own.
15 |
16 | ### Instructor
17 |
18 | Brandon Rich
19 |
20 | Application Integration Architect
21 |
22 |
23 |
24 | Check out my other courses on [LinkedIn Learning](https://www.linkedin.com/learning/instructors/brandon-rich).
25 |
26 | [lil-course-url]: https://www.linkedin.com/learning/advanced-aws-cloudformation-for-enterprise?dApp=59033956
27 | [lil-thumbnail-url]: https://media.licdn.com/dms/image/C560DAQG7yL6ZYpTN_g/learning-public-crop_288_512/0/1676575007930?e=2147483647&v=beta&t=a6INafXm9UNSh13rEQhOun8yv57P8QVYAWYYqX6Ph4Y
28 |
--------------------------------------------------------------------------------
/custom_resources/custom-resource-standalone.yml:
--------------------------------------------------------------------------------
1 | Description: >
2 | Simple custom resource demo
3 | Resources:
4 |
5 | LambdaExecutionRole:
6 | Type: AWS::IAM::Role
7 | Properties:
8 | AssumeRolePolicyDocument:
9 | Version: "2012-10-17"
10 | Statement:
11 | - Effect: Allow
12 | Principal:
13 | Service:
14 | - lambda.amazonaws.com
15 | Action:
16 | - 'sts:AssumeRole'
17 | Description: Allows Cloudformation to call services on your behalf. Slight change.
18 | ManagedPolicyArns:
19 | - arn:aws:iam::aws:policy/AmazonS3FullAccess
20 | - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
21 | RoleName: CloudformationServiceRole
22 |
23 | MyCustomResourceFunction:
24 | Type: AWS::Lambda::Function
25 | Properties:
26 | Code:
27 | ZipFile: |
28 | var response = require('cfn-response');
29 | var aws = require("aws-sdk");
30 |
31 | exports.handler = function(event, context) {
32 |
33 | var responseText = "starting function"
34 | var s3 = new aws.S3();
35 | var bucketName = event.ResourceProperties.BucketName;
36 | responseText = "got bucket name: " + bucketName
37 |
38 | if ( event.RequestType == 'Create' ) {
39 |
40 | s3.createBucket( { Bucket: bucketName }, function(err,data) {
41 | responseText = "created bucket " + bucketName
42 | })
43 |
44 | } else if ( event.RequestType == 'Delete' ) {
45 |
46 | s3.deleteBucket({Bucket: bucketName}, function(err,data){
47 | responseText = "deleted bucket " + bucketName
48 | })
49 |
50 | }
51 |
52 | var responseData = {msg: "hello world!", responseText: responseText};
53 | response.send(event, context, response.SUCCESS, responseData);
54 |
55 | };
56 | Handler: index.handler
57 | Timeout: 30
58 | Runtime: nodejs14.x
59 | Role: !GetAtt LambdaExecutionRole.Arn
60 |
61 | Outputs:
62 | MyCustomResourceArn:
63 | Description: The ARN of the custom resource, exported for use by others
64 | Value: !GetAtt MyCustomResourceFunction.Arn
65 | Export:
66 | Name: MyCustomResourceArn
67 |
--------------------------------------------------------------------------------
/basic_ec2/ec2_nginx.yml:
--------------------------------------------------------------------------------
1 | ---
2 | AWSTemplateFormatVersion: '2010-09-09'
3 | Description: A test stack for demonstrating Cloudformation
4 | Parameters:
5 | Subnet:
6 | Description: Subnet in which to place the resources
7 | Type: AWS::EC2::Subnet::Id
8 | # this type is not that helpful, since it just shows IDs
9 | KeyPair:
10 | Description: The key you'll use to log in to this instance
11 | Type: AWS::EC2::KeyPair::KeyName
12 | InstanceType:
13 | Description: The instance type for the Launch Config
14 | Type: String
15 | Default: t2.micro
16 | AllowedValues:
17 | - t2.nano
18 | - t2.micro
19 | - t2.small
20 | - t2.medium
21 | - t2.large
22 | Resources:
23 | SecurityGroupHTTPFromWorld:
24 | Type: AWS::EC2::SecurityGroup
25 | Properties:
26 | GroupDescription: Allows port 80 from the world
27 | GroupName: HTTPFromWorld
28 | # If we do not specify egress, the default
29 | # "all traffic" egress rule will be created.
30 | SecurityGroupIngress:
31 | - IpProtocol: tcp
32 | FromPort: 80
33 | ToPort: 80
34 | CidrIp: 0.0.0.0/0
35 | # Do I need VPC ID?
36 | Tags:
37 | - Key: Name
38 | Value: HTTPFromWorld
39 | SecurityGroupSSHFromWorld:
40 | Type: AWS::EC2::SecurityGroup
41 | Properties:
42 | GroupDescription: Allows port 22 from the world
43 | GroupName: SSHFromWorld
44 | # If we do not specify egress, the default
45 | # "all traffic" egress rule will be created.
46 | SecurityGroupIngress:
47 | - IpProtocol: tcp
48 | FromPort: 22
49 | ToPort: 22
50 | CidrIp: 0.0.0.0/0
51 | # Do I need VPC ID?
52 | Tags:
53 | - Key: Name
54 | Value: SSHFromWorld
55 | MyInstance:
56 | Type: AWS::EC2::Instance
57 | Properties:
58 | UserData:
59 | Fn::Base64: |
60 | #!/bin/bash -xe
61 | sudo amazon-linux-extras install -y nginx1
62 | sudo service nginx start
63 | ImageId: ami-0b0dcb5067f052a63
64 | KeyName: !Ref KeyPair
65 | InstanceType:
66 | Ref: InstanceType
67 | SecurityGroupIds:
68 | - !GetAtt SecurityGroupHTTPFromWorld.GroupId
69 | - !GetAtt SecurityGroupSSHFromWorld.GroupId
70 | SubnetId:
71 | Ref: Subnet
72 | Tags:
73 | - Key: Name
74 | Value: MyNginxInstance
75 | Outputs:
76 | InstanceIP:
77 | Description: The public IP of the instance
78 | Value: !GetAtt MyInstance.PublicIp
--------------------------------------------------------------------------------
/modules/webstack-module/schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "typeName": "LinkedIn::Learning::WebStack::MODULE",
3 | "description": "Schema for Module Fragment of type LinkedIn::Learning::WebStack::MODULE",
4 | "properties": {
5 | "Parameters": {
6 | "type": "object",
7 | "properties": {
8 | "Subnet": {
9 | "type": "object",
10 | "properties": {
11 | "Type": {
12 | "type": "string"
13 | },
14 | "Description": {
15 | "type": "string"
16 | }
17 | },
18 | "required": [
19 | "Type",
20 | "Description"
21 | ],
22 | "description": "Subnet in which to place the resources"
23 | },
24 | "InstanceType": {
25 | "type": "object",
26 | "properties": {
27 | "Type": {
28 | "type": "string"
29 | },
30 | "Description": {
31 | "type": "string"
32 | }
33 | },
34 | "required": [
35 | "Type",
36 | "Description"
37 | ],
38 | "description": "The instance type for the Launch Config"
39 | }
40 | }
41 | },
42 | "Resources": {
43 | "properties": {
44 | "SecurityGroupHTTPFromWorld": {
45 | "type": "object",
46 | "properties": {
47 | "Type": {
48 | "type": "string",
49 | "const": "AWS::EC2::SecurityGroup"
50 | },
51 | "Properties": {
52 | "type": "object"
53 | }
54 | }
55 | },
56 | "MyInstance": {
57 | "type": "object",
58 | "properties": {
59 | "Type": {
60 | "type": "string",
61 | "const": "AWS::EC2::Instance"
62 | },
63 | "Properties": {
64 | "type": "object"
65 | }
66 | }
67 | }
68 | },
69 | "type": "object",
70 | "additionalProperties": false
71 | }
72 | },
73 | "additionalProperties": true
74 | }
75 |
--------------------------------------------------------------------------------
/cfn-init/ec2-with-cfn-init.yml:
--------------------------------------------------------------------------------
1 | ---
2 | AWSTemplateFormatVersion: '2010-09-09'
3 | Description: A test stack for demonstrating Cloudformation
4 | Parameters:
5 | KeyPair:
6 | Description: The key you'll use to log in to this instance
7 | Type: AWS::EC2::KeyPair::KeyName
8 | Subnet:
9 | Description: Subnet in which to place the resources
10 | Type: AWS::EC2::Subnet::Id
11 | # this type is not that helpful, since it just shows IDs
12 | InstanceType:
13 | Description: The instance type for the Launch Config
14 | Type: String
15 | Default: t2.micro
16 | AllowedValues:
17 | - t2.nano
18 | - t2.micro
19 | - t2.small
20 | - t2.medium
21 | - t2.large
22 | Resources:
23 | SecurityGroupHTTPFromWorld:
24 | Type: AWS::EC2::SecurityGroup
25 | Properties:
26 | GroupDescription: Allows port 80 from the world
27 | GroupName: !Sub HTTPFromWorld-${AWS::StackName}
28 | # If we do not specify egress, the default
29 | # "all traffic" egress rule will be created.
30 | SecurityGroupIngress:
31 | - IpProtocol: tcp
32 | FromPort: 22
33 | ToPort: 22
34 | CidrIp: 0.0.0.0/0
35 | # Do I need VPC ID?
36 | MyInstance:
37 | Type: AWS::EC2::Instance
38 | CreationPolicy:
39 | ResourceSignal:
40 | Timeout: PT10M
41 | Metadata:
42 | AWS::Cloudformation::Init:
43 | config:
44 | packages:
45 | groups:
46 | users:
47 | sources:
48 | files:
49 | commands:
50 | - sudo amazon-linux-extras install -y nginx1
51 | services:
52 | systemd:
53 | nginx:
54 | enabled: true
55 | ensureRunning: true
56 | Properties:
57 | KeyName: !Ref KeyPair
58 | UserData:
59 | Fn::Base64: !Sub |
60 | #!/bin/bash -xe
61 | yum install -y aws-cfn-bootstrap
62 | /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource MyInstance --region ${AWS::Region}
63 | /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource MyInstance --region ${AWS::Region}
64 | ImageId: ami-0b0dcb5067f052a63
65 | InstanceType:
66 | Ref: InstanceType
67 | SecurityGroupIds:
68 | - !GetAtt SecurityGroupHTTPFromWorld.GroupId
69 | - !GetAtt SecurityGroupSSHFromWorld.GroupId
70 | SubnetId:
71 | Ref: Subnet
72 | SecurityGroupSSHFromWorld:
73 | Type: AWS::EC2::SecurityGroup
74 | Properties:
75 | GroupDescription: Allows port 22 from the world
76 | GroupName: !Sub SSHFromWorld-${AWS::StackName}
77 | # If we do not specify egress, the default
78 | # "all traffic" egress rule will be created.
79 | SecurityGroupIngress:
80 | - IpProtocol: tcp
81 | FromPort: 22
82 | ToPort: 22
83 | CidrIp: 0.0.0.0/0
84 | # Do I need VPC ID?
85 | Tags:
86 | - Key: Name
87 | Value: SSHFromWorld
88 | Outputs:
89 | InstanceIP:
90 | Description: The public IP of the instance
91 | Value: !GetAtt MyInstance.PublicIp
--------------------------------------------------------------------------------
/conditions/conditions.yml:
--------------------------------------------------------------------------------
1 | ---
2 | AWSTemplateFormatVersion: '2010-09-09'
3 | Description: A test stack for demonstrating Cloudformation
4 | Parameters:
5 | Subnet:
6 | Description: Subnet in which to place the resources
7 | Type: AWS::EC2::Subnet::Id
8 | # this type is not that helpful, since it just shows IDs
9 | KeyPair:
10 | Description: The key you'll use to log in to this instance
11 | Type: AWS::EC2::KeyPair::KeyName
12 | AllowSSH: # note, another approach would be to make the condition evaluate whether the user enters a keypair or not, but this is more explicit.
13 | Description: |
14 | Used to determine if we create and attach a security group for
15 | SSH and whether we attach a keypair to this instance
16 | Type: String
17 | AllowedValues:
18 | - true
19 | - false
20 | Environment:
21 | Description: |
22 | Specifies the environment for which you're deploying. Will
23 | be used to drive conditionals governing what resources to create
24 | Type: String
25 | AllowedValues:
26 | - production
27 | - development
28 | Conditions:
29 | IsProduction: !Equals [ !Ref Environment, 'production' ]
30 | AllowSSHCondition: !Equals [ !Ref AllowSSH, 'true' ] # condition should not have the exact same name as the input param
31 | Resources:
32 | SecurityGroupHTTPFromWorld:
33 | Type: AWS::EC2::SecurityGroup
34 | Properties:
35 | GroupDescription: Allows port 80 from the world
36 | GroupName: !Sub 'HTTPFromWorld-${AWS::StackName}'
37 | # If we do not specify egress, the default
38 | # "all traffic" egress rule will be created.
39 | SecurityGroupIngress:
40 | - IpProtocol: tcp
41 | FromPort: 80
42 | ToPort: 80
43 | CidrIp: 0.0.0.0/0
44 | # Do I need VPC ID?
45 | Tags:
46 | - Key: Name
47 | Value: HTTPFromWorld
48 | SecurityGroupSSHFromWorld:
49 | Type: AWS::EC2::SecurityGroup
50 | Condition: AllowSSHCondition
51 | Properties:
52 | GroupDescription: Allows port 22 from the world
53 | GroupName: !Sub 'SSHFromWorld-${AWS::StackName}'
54 | # If we do not specify egress, the default
55 | # "all traffic" egress rule will be created.
56 | SecurityGroupIngress:
57 | - IpProtocol: tcp
58 | FromPort: 22
59 | ToPort: 22
60 | CidrIp: 0.0.0.0/0
61 | # Do I need VPC ID?
62 | Tags:
63 | - Key: Name
64 | Value: SSHFromWorld
65 | MyInstance:
66 | Type: AWS::EC2::Instance
67 | Properties:
68 | UserData:
69 | Fn::Base64: |
70 | #!/bin/bash -xe
71 | sudo amazon-linux-extras install -y nginx1
72 | sudo service nginx start
73 | ImageId: ami-0b0dcb5067f052a63
74 | KeyName: !Ref KeyPair
75 | InstanceType: !If [ IsProduction, "t3.large", "t3.nano" ] # two different synxtax styles shown for !If
76 | SecurityGroupIds:
77 | - !GetAtt SecurityGroupHTTPFromWorld.GroupId
78 | - !If
79 | - AllowSSHCondition
80 | - !GetAtt SecurityGroupSSHFromWorld.GroupId
81 | - !Ref AWS::NoValue # when this is evaluated, it essentially erases this line, so we don't attempt to attach a non-existent security group
82 | # note that it has to be referenced like a parameter.
83 | SubnetId:
84 | Ref: Subnet
85 | Tags:
86 | - Key: Name
87 | Value: !Join
88 | - '-'
89 | - - 'MyNginxInstance'
90 | - !Ref Environment
91 | - !If [ AllowSSHCondition, 'withSSH', 'noSSH' ]
92 | Outputs:
93 | InstanceIP:
94 | Description: The public IP of the instance
95 | Value: !GetAtt MyInstance.PublicIp
--------------------------------------------------------------------------------
/parameters/all_parameter_types.yml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: 2010-09-09
2 | Description: |
3 | This template demos all the built-in AWS Cloudformation parameter types. They will appear in alpha order.
4 | It is possible to influence the appearance order by using AWS::CloudFormation::Interface, but it is fairly
5 | involved. It's best to limit your input parameters, lest different stacks made from the same template
6 | diverge too much from each other.
7 | Parameters:
8 | BucketName:
9 | Type: String
10 | Description: The name of a bucket. This appears near the user prompt.
11 | Default: MyDefaultBucketName
12 |
13 | AllowedValuesString:
14 | Type: String
15 | Description: This param uses AllowedValues
16 | AllowedValues:
17 | - red
18 | - blue
19 | - green
20 |
21 | SomeNumber:
22 | Type: Number
23 | Description: This is a Number type
24 |
25 | ListParam:
26 | Type: List
27 | Description: This uses type List expects a comma-separated list of integers
28 |
29 | CommaDelimitedListParam:
30 | Type: CommaDelimitedList
31 | Description: This is a type CommaDelimitedList and expects strings separated by commas.
32 |
33 | AvailabilityZoneName:
34 | Description: AWS::EC2::AvailabilityZone::Name
35 | Type: AWS::EC2::AvailabilityZone::Name
36 |
37 | # "Note that the AWS CloudFormation console doesn't show a drop-down
38 | # list of values for this parameter type." . So what good is it?
39 | EC2ImageId:
40 | Description: AWS::EC2::Image::Id
41 | Type: AWS::EC2::Image::Id
42 |
43 | EC2InstanceId:
44 | Description: AWS::EC2::Instance::Id
45 | Type: AWS::EC2::Instance::Id
46 |
47 | KeyPairName:
48 | Description: AWS::EC2::KeyPair::KeyName
49 | Type: AWS::EC2::KeyPair::KeyName
50 |
51 | SecurityGroupName:
52 | Description: AWS::EC2::SecurityGroup::GroupName
53 | Type: AWS::EC2::SecurityGroup::GroupName
54 |
55 | SecurityGroupId:
56 | Description: AWS::EC2::SecurityGroup::Id
57 | Type: AWS::EC2::SecurityGroup::Id
58 |
59 | SubnetID:
60 | Description: AWS::EC2::Subnet::Id
61 | Type: AWS::EC2::Subnet::Id
62 |
63 | VolumeID:
64 | Description: AWS::EC2::Volume::Id
65 | Type: AWS::EC2::Volume::Id
66 |
67 | VPCId:
68 | Description: AWS::EC2::VPC::Id
69 | Type: AWS::EC2::VPC::Id
70 |
71 | Route53HostedZoneId:
72 | Description: AWS::Route53::HostedZone::Id
73 | Type: AWS::Route53::HostedZone::Id
74 |
75 | AZNameList:
76 | Description: List
77 | Type: List
78 |
79 | EC2ImageIdList:
80 | Description: List
81 | Type: List
82 |
83 | EC2InstanceIdList:
84 | Description: List
85 | Type: List
86 |
87 | SecurityGroupNameList:
88 | Description: List
89 | Type: List
90 |
91 | SecurityGroupIdList:
92 | Description: List
93 | Type: List
94 |
95 | SubnetIdList:
96 | Description: List
97 | Type: List
98 |
99 | VolumeIdList:
100 | Description: List
101 | Type: List
102 |
103 | VPCIdList:
104 | Description: List
105 | Type: List
106 |
107 | Route53HostedZoneIdList:
108 | Description: List
109 | Type: List
110 |
111 | Resources:
112 | MyBucket:
113 | Type: AWS::S3::Bucket
114 | Properties:
115 | BucketName: !Ref BucketName
--------------------------------------------------------------------------------
/automation/v2/pipeline.yml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: 2010-09-09
2 | Description: |
3 |
4 | Parameters:
5 |
6 | RepoID:
7 | Description: The GitHub repo ID in the form username/repo_name
8 | Default: brandonrich/linkedin_learning_cloudformation
9 | Type: String
10 |
11 | TemplatePath:
12 | Description: The name of the GitHub repo
13 | Default: automation/template.yml
14 | Type: String
15 |
16 | BucketName:
17 | Description: |
18 | Provide the name of a bucket to be used by CodePipeline |
19 | when it fetches code from GitHub. Remember that bucket names must be globally unique.
20 | Type: String
21 |
22 | ConnectionARN:
23 | Description: The ARN of the CodeStar connection to GitHub
24 | Type: String
25 |
26 | Resources:
27 | MyBucket:
28 | Type: AWS::S3::Bucket
29 | Properties:
30 | BucketName: !Ref BucketName
31 |
32 | MyPipeline:
33 | Type: AWS::CodePipeline::Pipeline
34 | Properties:
35 | ArtifactStore:
36 | Type: S3
37 | Location: !Ref MyBucket
38 | Name: MyInfrastructureAsCodePipeline
39 | RestartExecutionOnUpdate: true
40 | RoleArn: !GetAtt CodePipelineServiceRole.Arn
41 | Stages:
42 | # each of these is called a StageDeclaration
43 | # See https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codepipeline-pipeline-stages.html
44 | - Name: Source
45 | Actions:
46 | # each of these is called an ActionDeclaration.
47 | # see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codepipeline-pipeline-stages-actions.html
48 | - Name: Source
49 | InputArtifacts: []
50 | ActionTypeId:
51 | # For all available action types and their values, see
52 | # https://docs.aws.amazon.com/codepipeline/latest/userguide/reference-pipeline-structure.html#actions-valid-providers
53 | Category: Source
54 | Owner: AWS
55 | Version: 1 # This is actually GitHub v2, but since it's through CodeStar, it's v1.
56 | Provider: CodeStarSourceConnection
57 | OutputArtifacts:
58 | - Name: SourceCode
59 | Configuration:
60 | # These params are described here:
61 | # https://docs.aws.amazon.com/codepipeline/latest/userguide/action-reference-CodestarConnectionSource.html
62 | ConnectionArn: !Ref ConnectionARN
63 | FullRepositoryId: !Ref RepoID
64 | BranchName: main
65 | RunOrder: 1
66 | # Build and Deploy, etc., stages would follow. Here is an example
67 | - Name: Deploy
68 | Actions:
69 | - Name: CloudFormationDeploy
70 | ActionTypeId:
71 | Category: Deploy
72 | Owner: AWS
73 | Provider: CloudFormation
74 | Version: '1'
75 | InputArtifacts:
76 | - Name: SourceCode
77 | Configuration:
78 | ActionMode: CREATE_UPDATE
79 | Capabilities: CAPABILITY_IAM # this declares that the template being built will contain IAM changes. I think this triggers that assertion checkbox for th euser.
80 | # since we will not be running this manually, we need to provide
81 | # a role to permit the automated Cloudformation to do things
82 | RoleArn: !GetAtt CloudformationServiceRole.Arn
83 | StackName: MyGitHubAutomatedTemplate
84 | # this refers to the input artifact by name. Too bad we can't declare a
85 | # constant in the app, so we would not be repeating it in a hard-coded fashion.
86 | TemplatePath: !Sub
87 | - 'SourceCode::${template_path}'
88 | - template_path: !Ref TemplatePath
89 | RunOrder: 1
90 |
91 | CodePipelineServiceRole:
92 | Type: AWS::IAM::Role
93 | Properties:
94 | AssumeRolePolicyDocument:
95 | Version: "2012-10-17"
96 | Statement:
97 | - Effect: Allow
98 | Principal:
99 | Service:
100 | - codepipeline.amazonaws.com
101 | Action:
102 | - 'sts:AssumeRole'
103 | Description: Allows CodePipeline to call services on your behalf.
104 | Policies:
105 | - PolicyName: SuperAdminEverything
106 | PolicyDocument:
107 | Version: "2012-10-17"
108 | Statement:
109 | - Effect: Allow
110 | Action: '*'
111 | Resource: '*'
112 | RoleName: CodePipelineServiceRole
113 |
114 | CloudformationServiceRole:
115 | Type: AWS::IAM::Role
116 | Properties:
117 | AssumeRolePolicyDocument:
118 | Version: "2012-10-17"
119 | Statement:
120 | - Effect: Allow
121 | Principal:
122 | Service:
123 | - cloudformation.amazonaws.com
124 | Action:
125 | - 'sts:AssumeRole'
126 | Description: Allows Cloudformation to call services on your behalf. Slight change.
127 | ManagedPolicyArns:
128 | - arn:aws:iam::aws:policy/AmazonS3FullAccess
129 | - arn:aws:iam::aws:policy/AmazonEC2FullAccess # this should allow not only creating instances, but security groups as well
130 | RoleName: CloudformationServiceRole
131 |
--------------------------------------------------------------------------------
/automation/v1/pipeline.yml:
--------------------------------------------------------------------------------
1 | AWSTemplateFormatVersion: 2010-09-09
2 | Description: |
3 |
4 | Parameters:
5 |
6 | RepoOwner:
7 | Description: The username of the owner of the repo
8 | Type: String
9 |
10 | RepoName:
11 | Description: The name of the GitHub repo
12 | Type: String
13 |
14 | TemplatePath:
15 | Description: The name of the GitHub repo
16 | Default: automation/template.yml
17 | Type: String
18 |
19 | BucketName:
20 | Description: |
21 | Provide the name of a bucket to be used by CodePipeline |
22 | when it fetches code from GitHub. Remember that bucket names must be globally unique.
23 | Type: String
24 |
25 | Resources:
26 | MyBucket:
27 | Type: AWS::S3::Bucket
28 | Properties:
29 | BucketName: !Ref BucketName
30 |
31 | MyPipeline:
32 | Type: AWS::CodePipeline::Pipeline
33 | Properties:
34 | ArtifactStore:
35 | Type: S3
36 | Location: !Ref MyBucket
37 | Name: MyInfrastructureAsCodePipeline
38 | RestartExecutionOnUpdate: true
39 | RoleArn: !GetAtt CodePipelineServiceRole.Arn
40 | Stages:
41 | # each of these is called a StageDeclaration
42 | # See https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codepipeline-pipeline-stages.html
43 | - Name: Source
44 | Actions:
45 | # each of these is called an ActionDeclaration.
46 | # see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-codepipeline-pipeline-stages-actions.html
47 | - Name: Source
48 | InputArtifacts: []
49 | ActionTypeId:
50 | # For all available action types and their values, see
51 | # https://docs.aws.amazon.com/codepipeline/latest/userguide/reference-pipeline-structure.html#actions-valid-providers
52 | Category: Source
53 | Owner: ThirdParty
54 | Version: 1 # There is a v2 that is recommended when using the GUI, so is there a newer way to do this in CF?
55 | Provider: GitHub
56 | OutputArtifacts:
57 | - Name: SourceCode
58 | Configuration:
59 | Owner: !Ref RepoOwner # could we figure this out by parsing a GitHub URL?
60 | Repo: !Ref RepoName
61 | Branch: main
62 | PollForSourceChanges: false
63 | OAuthToken: '{{resolve:secretsmanager:GitHubCloudformationDemo:SecretString:access_token}}'
64 | RunOrder: 1
65 | # Build and Deploy, etc., stages would follow. Here is an example
66 | - Name: Deploy
67 | Actions:
68 | - Name: CloudFormationDeploy
69 | ActionTypeId:
70 | Category: Deploy
71 | Owner: AWS
72 | Provider: CloudFormation
73 | Version: '1'
74 | InputArtifacts:
75 | - Name: SourceCode
76 | Configuration:
77 | ActionMode: CREATE_UPDATE
78 | Capabilities: CAPABILITY_IAM # this declares that the template being built will contain IAM changes. I think this triggers that assertion checkbox for th euser.
79 | # since we will not be running this manually, we need to provide
80 | # a role to permit the automated Cloudformation to do things
81 | RoleArn: !GetAtt CloudformationServiceRole.Arn
82 | StackName: MyGitHubAutomatedTemplate
83 | # this refers to the input artifact by name. Too bad we can't declare a
84 | # constant in the app, so we would not be repeating it in a hard-coded fashion.
85 | TemplatePath: !Sub
86 | - 'SourceCode::${template_path}'
87 | - template_path: !Ref TemplatePath
88 | RunOrder: 1
89 |
90 | MyGitHubWebhook:
91 | Type: 'AWS::CodePipeline::Webhook'
92 | Properties:
93 | Authentication: GITHUB_HMAC
94 | AuthenticationConfiguration:
95 | SecretToken: '{{resolve:secretsmanager:GitHubCloudformationDemo:SecretString:access_token}}'
96 | RegisterWithThirdParty: 'true'
97 | Filters:
98 | - JsonPath: "$.ref"
99 | MatchEquals: refs/heads/main
100 | TargetPipeline: !Ref MyPipeline
101 | TargetAction: Source
102 | TargetPipelineVersion: !GetAtt MyPipeline.Version
103 |
104 | CodePipelineServiceRole:
105 | Type: AWS::IAM::Role
106 | Properties:
107 | AssumeRolePolicyDocument:
108 | Version: "2012-10-17"
109 | Statement:
110 | - Effect: Allow
111 | Principal:
112 | Service:
113 | - codepipeline.amazonaws.com
114 | Action:
115 | - 'sts:AssumeRole'
116 | Description: Allows CodePipeline to call services on your behalf.
117 | Policies:
118 | - PolicyName: SuperAdminEverything
119 | PolicyDocument:
120 | Version: "2012-10-17"
121 | Statement:
122 | - Effect: Allow
123 | Action: '*'
124 | Resource: '*'
125 | RoleName: CodePipelineServiceRole
126 |
127 | CloudformationServiceRole:
128 | Type: AWS::IAM::Role
129 | Properties:
130 | AssumeRolePolicyDocument:
131 | Version: "2012-10-17"
132 | Statement:
133 | - Effect: Allow
134 | Principal:
135 | Service:
136 | - cloudformation.amazonaws.com
137 | Action:
138 | - 'sts:AssumeRole'
139 | Description: Allows Cloudformation to call services on your behalf. Slight change.
140 | ManagedPolicyArns:
141 | - arn:aws:iam::aws:policy/AmazonS3FullAccess
142 | - arn:aws:iam::aws:policy/AmazonEC2FullAccess # this should allow not only creating instances, but security groups as well
143 | RoleName: CloudformationServiceRole
144 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | LinkedIn Learning Exercise Files License Agreement
2 | ==================================================
3 |
4 | This License Agreement (the "Agreement") is a binding legal agreement
5 | between you (as an individual or entity, as applicable) and LinkedIn
6 | Corporation (“LinkedIn”). By downloading or using the LinkedIn Learning
7 | exercise files in this repository (“Licensed Materials”), you agree to
8 | be bound by the terms of this Agreement. If you do not agree to these
9 | terms, do not download or use the Licensed Materials.
10 |
11 | 1. License.
12 | - a. Subject to the terms of this Agreement, LinkedIn hereby grants LinkedIn
13 | members during their LinkedIn Learning subscription a non-exclusive,
14 | non-transferable copyright license, for internal use only, to 1) make a
15 | reasonable number of copies of the Licensed Materials, and 2) make
16 | derivative works of the Licensed Materials for the sole purpose of
17 | practicing skills taught in LinkedIn Learning courses.
18 | - b. Distribution. Unless otherwise noted in the Licensed Materials, subject
19 | to the terms of this Agreement, LinkedIn hereby grants LinkedIn members
20 | with a LinkedIn Learning subscription a non-exclusive, non-transferable
21 | copyright license to distribute the Licensed Materials, except the
22 | Licensed Materials may not be included in any product or service (or
23 | otherwise used) to instruct or educate others.
24 |
25 | 2. Restrictions and Intellectual Property.
26 | - a. You may not to use, modify, copy, make derivative works of, publish,
27 | distribute, rent, lease, sell, sublicense, assign or otherwise transfer the
28 | Licensed Materials, except as expressly set forth above in Section 1.
29 | - b. Linkedin (and its licensors) retains its intellectual property rights
30 | in the Licensed Materials. Except as expressly set forth in Section 1,
31 | LinkedIn grants no licenses.
32 | - c. You indemnify LinkedIn and its licensors and affiliates for i) any
33 | alleged infringement or misappropriation of any intellectual property rights
34 | of any third party based on modifications you make to the Licensed Materials,
35 | ii) any claims arising from your use or distribution of all or part of the
36 | Licensed Materials and iii) a breach of this Agreement. You will defend, hold
37 | harmless, and indemnify LinkedIn and its affiliates (and our and their
38 | respective employees, shareholders, and directors) from any claim or action
39 | brought by a third party, including all damages, liabilities, costs and
40 | expenses, including reasonable attorneys’ fees, to the extent resulting from,
41 | alleged to have resulted from, or in connection with: (a) your breach of your
42 | obligations herein; or (b) your use or distribution of any Licensed Materials.
43 |
44 | 3. Open source. This code may include open source software, which may be
45 | subject to other license terms as provided in the files.
46 |
47 | 4. Warranty Disclaimer. LINKEDIN PROVIDES THE LICENSED MATERIALS ON AN “AS IS”
48 | AND “AS AVAILABLE” BASIS. LINKEDIN MAKES NO REPRESENTATION OR WARRANTY,
49 | WHETHER EXPRESS OR IMPLIED, ABOUT THE LICENSED MATERIALS, INCLUDING ANY
50 | REPRESENTATION THAT THE LICENSED MATERIALS WILL BE FREE OF ERRORS, BUGS OR
51 | INTERRUPTIONS, OR THAT THE LICENSED MATERIALS ARE ACCURATE, COMPLETE OR
52 | OTHERWISE VALID. TO THE FULLEST EXTENT PERMITTED BY LAW, LINKEDIN AND ITS
53 | AFFILIATES DISCLAIM ANY IMPLIED OR STATUTORY WARRANTY OR CONDITION, INCLUDING
54 | ANY IMPLIED WARRANTY OR CONDITION OF MERCHANTABILITY OR FITNESS FOR A
55 | PARTICULAR PURPOSE, AVAILABILITY, SECURITY, TITLE AND/OR NON-INFRINGEMENT.
56 | YOUR USE OF THE LICENSED MATERIALS IS AT YOUR OWN DISCRETION AND RISK, AND
57 | YOU WILL BE SOLELY RESPONSIBLE FOR ANY DAMAGE THAT RESULTS FROM USE OF THE
58 | LICENSED MATERIALS TO YOUR COMPUTER SYSTEM OR LOSS OF DATA. NO ADVICE OR
59 | INFORMATION, WHETHER ORAL OR WRITTEN, OBTAINED BY YOU FROM US OR THROUGH OR
60 | FROM THE LICENSED MATERIALS WILL CREATE ANY WARRANTY OR CONDITION NOT
61 | EXPRESSLY STATED IN THESE TERMS.
62 |
63 | 5. Limitation of Liability. LINKEDIN SHALL NOT BE LIABLE FOR ANY INDIRECT,
64 | INCIDENTAL, SPECIAL, PUNITIVE, CONSEQUENTIAL OR EXEMPLARY DAMAGES, INCLUDING
65 | BUT NOT LIMITED TO, DAMAGES FOR LOSS OF PROFITS, GOODWILL, USE, DATA OR OTHER
66 | INTANGIBLE LOSSES . IN NO EVENT WILL LINKEDIN'S AGGREGATE LIABILITY TO YOU
67 | EXCEED $100. THIS LIMITATION OF LIABILITY SHALL:
68 | - i. APPLY REGARDLESS OF WHETHER (A) YOU BASE YOUR CLAIM ON CONTRACT, TORT,
69 | STATUTE, OR ANY OTHER LEGAL THEORY, (B) WE KNEW OR SHOULD HAVE KNOWN ABOUT
70 | THE POSSIBILITY OF SUCH DAMAGES, OR (C) THE LIMITED REMEDIES PROVIDED IN THIS
71 | SECTION FAIL OF THEIR ESSENTIAL PURPOSE; AND
72 | - ii. NOT APPLY TO ANY DAMAGE THAT LINKEDIN MAY CAUSE YOU INTENTIONALLY OR
73 | KNOWINGLY IN VIOLATION OF THESE TERMS OR APPLICABLE LAW, OR AS OTHERWISE
74 | MANDATED BY APPLICABLE LAW THAT CANNOT BE DISCLAIMED IN THESE TERMS.
75 |
76 | 6. Termination. This Agreement automatically terminates upon your breach of
77 | this Agreement or termination of your LinkedIn Learning subscription. On
78 | termination, all licenses granted under this Agreement will terminate
79 | immediately and you will delete the Licensed Materials. Sections 2-7 of this
80 | Agreement survive any termination of this Agreement. LinkedIn may discontinue
81 | the availability of some or all of the Licensed Materials at any time for any
82 | reason.
83 |
84 | 7. Miscellaneous. This Agreement will be governed by and construed in
85 | accordance with the laws of the State of California without regard to conflict
86 | of laws principles. The exclusive forum for any disputes arising out of or
87 | relating to this Agreement shall be an appropriate federal or state court
88 | sitting in the County of Santa Clara, State of California. If LinkedIn does
89 | not act to enforce a breach of this Agreement, that does not mean that
90 | LinkedIn has waived its right to enforce this Agreement. The Agreement does
91 | not create a partnership, agency relationship, or joint venture between the
92 | parties. Neither party has the power or authority to bind the other or to
93 | create any obligation or responsibility on behalf of the other. You may not,
94 | without LinkedIn’s prior written consent, assign or delegate any rights or
95 | obligations under these terms, including in connection with a change of
96 | control. Any purported assignment and delegation shall be ineffective. The
97 | Agreement shall bind and inure to the benefit of the parties, their respective
98 | successors and permitted assigns. If any provision of the Agreement is
99 | unenforceable, that provision will be modified to render it enforceable to the
100 | extent possible to give effect to the parties’ intentions and the remaining
101 | provisions will not be affected. This Agreement is the only agreement between
102 | you and LinkedIn regarding the Licensed Materials, and supersedes all prior
103 | agreements relating to the Licensed Materials.
104 |
105 | Last Updated: March 2019
106 |
--------------------------------------------------------------------------------