cloud:aws:cloudformation
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
cloud:aws:cloudformation [2023/11/01 07:13] – removed - external edit (Unknown date) 127.0.0.1 | cloud:aws:cloudformation [2023/12/05 15:02] (current) – skipidar | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ===== CloudFormation ===== | ||
+ | |||
+ | ==== Why is Terraform better? ==== | ||
+ | |||
+ | * " | ||
+ | * " | ||
+ | * " | ||
+ | * CloudFormation has a very NOT user friendly lifecycle. Forcing to "is in ROLLBACK_COMPLETE state and can not be updated" | ||
+ | * Minor. Cloudformation " | ||
+ | * Cloudformation support of moving resources between stacks is very chatty | ||
+ | |||
+ | |||
+ | ==== Deploying with cloudformation ==== | ||
+ | |||
+ | If using nested-stacks first you need a bucket, | ||
+ | into which you will package nested stacks. | ||
+ | |||
+ | <sxh yaml> | ||
+ | AWSTemplateFormatVersion: | ||
+ | Description: | ||
+ | Resources: | ||
+ | |||
+ | MyS3SubstackBucket: | ||
+ | Type: AWS:: | ||
+ | Properties: | ||
+ | BucketName: my-alf-s3-package-bucket-2023-12-05 | ||
+ | AccessControl: | ||
+ | Tags: | ||
+ | - Key: Purpose | ||
+ | Value: CF stacks bucket | ||
+ | |||
+ | |||
+ | MyBucketPolicy: | ||
+ | Type: AWS:: | ||
+ | Properties: | ||
+ | Bucket: !Ref MyS3SubstackBucket | ||
+ | PolicyDocument: | ||
+ | Statement: | ||
+ | - Sid: AllowCloudFormationAccess | ||
+ | Effect: Allow | ||
+ | Principal: | ||
+ | Service: cloudformation.amazonaws.com | ||
+ | Action: s3:* | ||
+ | Resource: !Join | ||
+ | - '' | ||
+ | - - ' | ||
+ | - !Ref MyS3SubstackBucket | ||
+ | - /* | ||
+ | </ | ||
+ | |||
+ | now deploy the bucket | ||
+ | <sxh shell> | ||
+ | |||
+ | FILENAME=" | ||
+ | STACKNAME=" | ||
+ | REGION=" | ||
+ | |||
+ | # validate | ||
+ | aws cloudformation validate-template --template-body file:// | ||
+ | |||
+ | |||
+ | # check the change set | ||
+ | aws cloudformation deploy --stack-name $STACKNAME --template-file $FILENAME --region $REGION --no-execute-changeset | ||
+ | |||
+ | |||
+ | # execute via " | ||
+ | aws cloudformation deploy --stack-name $STACKNAME --template-file $FILENAME --region $REGION | ||
+ | |||
+ | |||
+ | # delete stack | ||
+ | # aws cloudformation delete-stack --stack-name $STACKNAME | ||
+ | </ | ||
+ | |||
+ | |||
+ | parent1.cloudformation.yaml | ||
+ | <sxh yaml> | ||
+ | AWSTemplateFormatVersion: | ||
+ | Description: | ||
+ | |||
+ | Parameters: | ||
+ | |||
+ | VpcIdParameter: | ||
+ | Type: String | ||
+ | Default: " | ||
+ | | ||
+ | packageBucket: | ||
+ | Type: String | ||
+ | Default: " | ||
+ | |||
+ | Resources: | ||
+ | |||
+ | SubStack1: | ||
+ | Type: AWS:: | ||
+ | Properties: | ||
+ | TemplateURL: | ||
+ | Parameters: | ||
+ | VpcId: !Ref VpcIdParameter | ||
+ | |||
+ | </ | ||
+ | |||
+ | substack.helloworld.cloudformation.yaml | ||
+ | <sxh yaml> | ||
+ | AWSTemplateFormatVersion: | ||
+ | Description: | ||
+ | |||
+ | Parameters: | ||
+ | VpcId: | ||
+ | Type: String | ||
+ | |||
+ | |||
+ | Resources: | ||
+ | |||
+ | MySecurityGroup: | ||
+ | Type: AWS:: | ||
+ | Properties: | ||
+ | GroupDescription: | ||
+ | VpcId: !Ref VpcId | ||
+ | SecurityGroupIngress: | ||
+ | - IpProtocol: tcp | ||
+ | FromPort: 80 | ||
+ | ToPort: 80 | ||
+ | CidrIp: 0.0.0.0/0 # Example: Allowing HTTP traffic from anywhere (Please adjust for your use case) | ||
+ | Tags: | ||
+ | - Key: Name | ||
+ | Value: MySecurityGroup | ||
+ | |||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | now you can package the stack | ||
+ | |||
+ | * the sub-stacks will end up in the package-bucket. | ||
+ | * a new file `packaged-root-template.yaml` is generated, where the `TemplateURL` field is replaced by s3 references. | ||
+ | * you can deploy the parent stack and see nested stacks being deployed too. | ||
+ | |||
+ | |||
+ | |||
+ | <sxh shell> | ||
+ | set -e | ||
+ | |||
+ | FILENAME=" | ||
+ | STACKNAME=" | ||
+ | REGION=" | ||
+ | PACKAGEBUCKET=" | ||
+ | |||
+ | |||
+ | # validate | ||
+ | # aws cloudformation validate-template --template-body file:// | ||
+ | |||
+ | |||
+ | # package uploading substacks | ||
+ | rm packaged-root-template.yaml | ||
+ | aws cloudformation package --template-file $FILENAME --s3-bucket $PACKAGEBUCKET | ||
+ | |||
+ | |||
+ | |||
+ | # check the change set, dont execute : " | ||
+ | # aws cloudformation deploy --stack-name $STACKNAME --template-file $FILENAME --region $REGION --no-execute-changeset | ||
+ | |||
+ | # arn of change set is printed, here arn: | ||
+ | |||
+ | # can see change-set | ||
+ | # aws cloudformation describe-change-set --change-set-name arn: | ||
+ | |||
+ | # can continue via | ||
+ | # aws cloudformation execute-change-set --change-set-name arn: | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | # execute via " | ||
+ | aws cloudformation deploy --stack-name $STACKNAME --template-file packaged-root-template.yaml --region $REGION | ||
+ | |||
+ | |||
+ | # delete stack | ||
+ | # aws cloudformation delete-stack --stack-name $STACKNAME | ||
+ | </ | ||
+ | |||
+ | |||
+ | ==== Structure ==== | ||
+ | |||
+ | ==Parameters== | ||
+ | User parameters. | ||
+ | |||
+ | ==Resources== | ||
+ | The resources, which should be created, like EC2 Instances, SecurityGroups. | ||
+ | |||
+ | https:// | ||
+ | |||
+ | |Instance|https:// | ||
+ | |SecurityGroup| https:// | ||
+ | |||
+ | ==Outputs== | ||
+ | What is printed inside the AWS console, after the cloud formation script is completed. | ||
+ | ==== Instance Initiation ==== | ||
+ | Initiation is described inside of the following block: [[https:// | ||
+ | |||
+ | This block is NOT executed on the instance automatgically. | ||
+ | Instead a special script must be executed within the UserData block. | ||
+ | |||
+ | <sxh js> | ||
+ | " | ||
+ | " | ||
+ | "# | ||
+ | " | ||
+ | "# | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | "# | ||
+ | "/ | ||
+ | " | ||
+ | "# | ||
+ | "/ | ||
+ | ]]} | ||
+ | </ | ||
+ | |||
+ | For details see: https:// | ||
+ | |||
+ | == cfn-init== | ||
+ | This is an amazon provided script, which triggers the " | ||
+ | https:// | ||
+ | |||
+ | <sxh js> | ||
+ | "/ | ||
+ | </ | ||
+ | |||
+ | |||
+ | The most interesting usage of this script is, that it may be triggered directly on the named instance, to do the debugging. | ||
+ | |||
+ | |||
+ | |||
+ | == cfn-signal== | ||
+ | This script sends a signal to a [[https:// | ||
+ | |||
+ | It in pair with the cfn-init it signals the completion of an instance creation. | ||
+ | |||
+ | < | ||
+ | / | ||
+ | </ | ||
+ | |||
+ | ==== Execution Order ==== | ||
+ | |||
+ | ==Different Resources== | ||
+ | The creation of the resources may happen in parallel. | ||
+ | Cloud formation waits if resources are marked as a dependent. | ||
+ | |||
+ | You can easily define the order, by specifying the [[https:// | ||
+ | |||
+ | ==Instance initiation== | ||
+ | Instance initiation is triggered from UserData. So UserData comes first. | ||
+ | < | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | : | ||
+ | }, | ||
+ | " | ||
+ | : | ||
+ | }, | ||
+ | " | ||
+ | : | ||
+ | }, | ||
+ | " | ||
+ | : | ||
+ | }, | ||
+ | " | ||
+ | : | ||
+ | }, | ||
+ | " | ||
+ | : | ||
+ | }, | ||
+ | " | ||
+ | : | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | ==== Download from S3 ==== | ||
+ | A lot is necessary to download from S3 using a role autehnticaiton. | ||
+ | |||
+ | A role (here alf-digital-s3) is required with following settings: | ||
+ | * read access to S3 | ||
+ | * trust relationsship to the ec2 service | ||
+ | |||
+ | |||
+ | Here are the details: https:// | ||
+ | |||
+ | < | ||
+ | { | ||
+ | " | ||
+ | ... | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | } | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | "/ | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | }, | ||
+ | " | ||
+ | ... | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | } | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }] | ||
+ | } | ||
+ | } | ||
+ | }, | ||
+ | |||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== Use !Sub in commands ==== | ||
+ | |||
+ | To reference cloudformation arguments in commands - use the " | ||
+ | < | ||
+ | Resources: | ||
+ | Parameters: | ||
+ | S3Prefix: | ||
+ | AllowedPattern: | ||
+ | ^s3:// | ||
+ | Description: | ||
+ | Type: String | ||
+ | |||
+ | ... | ||
+ | 04_execute: | ||
+ | command: !Sub | | ||
+ | ansible-playbook / | ||
+ | </ | ||
+ | |||
+ | |||
+ | ==== Domain Join PowerShell Script | ||
+ | |||
+ | When creating a windows-machine - it is possible to join the domain via a PowerShell script, instead of using the wizard inside the Amazon console. | ||
+ | |||
+ | |||
+ | It is useful if some initiation of the domain must be done. | ||
+ | Since the domain-joining via the Wizard seems to happen AFTER the user-data are executed - to be able to modify the Active Directory from the user-data we must manually join the domain. | ||
+ | |||
+ | For that we do the following: | ||
+ | - Create a Microsoft Domain | ||
+ | - Retrieve the **Microsoft Domain DNS-Server Ips** from the Microsoft Domain (" | ||
+ | - reconfigure the new instance' | ||
+ | - now, with the new DNS the domain is resolvable - **join the domain** | ||
+ | - do the modifications: | ||
+ | - finally undo the **< | ||
+ | |||
+ | == Restart duing the powershell userdata script == | ||
+ | During the script, after the domain join the machine will be restarted. | ||
+ | So the execution of the script will be stopped. | ||
+ | In order to go on with the userdata script execution after the restart one should use **< | ||
+ | This will make the script persistent and executed after the restart again. | ||
+ | |||
+ | |||
+ | |||
+ | <sxh powershell> | ||
+ | |||
+ | < | ||
+ | |||
+ | echo " | ||
+ | |||
+ | #install snapin | ||
+ | Install-WindowsFeature -Name AD-Domain-Services, | ||
+ | |||
+ | ##### JOIN THE DOMAIN ##### | ||
+ | |||
+ | #Retrieve the AWS instance ID, keep trying until the metadata is available | ||
+ | $instanceID = " | ||
+ | while ($instanceID -NotLike " | ||
+ | | ||
+ | | ||
+ | echo " | ||
+ | } | ||
+ | echo " | ||
+ | |||
+ | |||
+ | # set the domain-own DNS servers to the network adapter, so that the domain name can be resolved and we can join | ||
+ | echo " | ||
+ | $adapter = Get-NetAdapter -Name " | ||
+ | Set-DnsClientServerAddress -InterfaceAlias $adapter.Name -ServerAddresses (" | ||
+ | echo " | ||
+ | |||
+ | |||
+ | # join the domain now | ||
+ | echo " | ||
+ | $domain = " | ||
+ | $username = " | ||
+ | $password = " | ||
+ | $cred = New-Object -typename System.Management.Automation.PSCredential($username, | ||
+ | |||
+ | Try { | ||
+ | $partOfDomain = (Get-WmiObject -Class Win32_ComputerSystem).PartOfDomain | ||
+ | if ( $partOfDomain ) { | ||
+ | echo " | ||
+ | } | ||
+ | Else { | ||
+ | echo "Not yet part of the domain - start joining" | ||
+ | Rename-Computer -NewName $instanceID -Force | ||
+ | Start-Sleep -s 5 | ||
+ | Add-Computer -DomainName basicm.local -OUPath " | ||
+ | } | ||
+ | |||
+ | } | ||
+ | Catch{ | ||
+ | echo $_.Exception | Out-File c: | ||
+ | } | ||
+ | echo " | ||
+ | |||
+ | |||
+ | |||
+ | # wait until the machine joins the domain | ||
+ | echo "Now waiting for the instance to be part of the domain" | ||
+ | $partOfDomain = $False | ||
+ | while ( -not $partOfDomain ) { | ||
+ | | ||
+ | | ||
+ | echo " | ||
+ | } | ||
+ | echo " | ||
+ | |||
+ | |||
+ | # add a basic user | ||
+ | echo "Now modify the Active Directory" | ||
+ | Try { | ||
+ | echo " | ||
+ | New-ADOrganizationalUnit -Name Groups -Path " | ||
+ | NEW-ADGroup –name " | ||
+ | |||
+ | |||
+ | |||
+ | echo " | ||
+ | New-ADUser -GivenName First -Surname User -Name first.user -Path " | ||
+ | $user = Get-ADUser " | ||
+ | |||
+ | echo " | ||
+ | Set-ADAccountPassword $user -NewPassword (ConvertTo-SecureString " | ||
+ | Enable-ADAccount -Identity $user -Credential $cred | ||
+ | |||
+ | echo " | ||
+ | $group = Get-ADGroup " | ||
+ | Add-ADGroupMember $group -Members $user -Credential $cred | ||
+ | $group = Get-ADGroup " | ||
+ | Add-ADGroupMember $group -Members $user -Credential $cred | ||
+ | |||
+ | echo " | ||
+ | New-ADUser -Name s000001 -Path " | ||
+ | $user = Get-ADUser " | ||
+ | |||
+ | echo "Set the password and enable" | ||
+ | Set-ADAccountPassword $user -NewPassword (ConvertTo-SecureString " | ||
+ | Enable-ADAccount -Identity $user -Credential $cred | ||
+ | } | ||
+ | Catch{ | ||
+ | echo $_.Exception | Out-File c: | ||
+ | } | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | # disable the userdata running task scheduler task, now when we are done | ||
+ | echo "Now Disable the task" | Out-File c: | ||
+ | Try { | ||
+ | & SCHTASKS /Change /DISABLE /TN " | ||
+ | } | ||
+ | Catch{ | ||
+ | echo $_.Exception | Out-File c: | ||
+ | } | ||
+ | |||
+ | |||
+ | echo " | ||
+ | |||
+ | </ | ||
+ | < | ||
+ | |||
+ | </ | ||
+ | |||
+ | |||
+ | To convert the above powershell script to a cloudformation capable UserData block do: | ||
+ | * escape all backslashes with an additional backslash: \\ | ||
+ | * escape all doublequotes with a backslash: | ||
+ | * prepend at the beginning of every line a doublequote: | ||
+ | * append at the end of every line a newline, doublequote, | ||
+ | * replace all tabs by spaces | ||
+ | * remove the comma from the last line | ||
+ | * replace the parts, which should be passed as parameters to the template by references: \\ " | ||
+ | |||
+ | Result: | ||
+ | <sxh json> | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | [" < | ||
+ | " | ||
+ | " echo \" | ||
+ | " | ||
+ | " #install snapin \n ", | ||
+ | " Install-WindowsFeature -Name AD-Domain-Services, | ||
+ | " | ||
+ | " ##### JOIN THE DOMAIN ##### \n ", | ||
+ | " | ||
+ | " #Retrieve the AWS instance ID, keep trying until the metadata is available \n ", | ||
+ | " $instanceID = \" | ||
+ | " while ($instanceID -NotLike \" | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " } \n ", | ||
+ | " echo \" | ||
+ | " | ||
+ | " | ||
+ | " # set the domain-own DNS servers to the network adapter, so that the domain name can be resolved and we can join \n ", | ||
+ | " echo \" | ||
+ | " $adapter = Get-NetAdapter -Name \" | ||
+ | " Set-DnsClientServerAddress -InterfaceAlias $adapter.Name -ServerAddresses (\"", | ||
+ | " echo \" | ||
+ | " | ||
+ | " | ||
+ | " # join the domain now \n ", | ||
+ | " echo \" | ||
+ | " $domain = \" | ||
+ | " $username = \" | ||
+ | " $password = \"", | ||
+ | " $cred = New-Object -typename System.Management.Automation.PSCredential($username, | ||
+ | " | ||
+ | " Try { \n ", | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " } \n ", | ||
+ | " Catch{ \n ", | ||
+ | " | ||
+ | " } \n ", | ||
+ | " echo \" | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " # wait until the machine joins the domain \n ", | ||
+ | " echo \"Now waiting for the instance to be part of the domain\" | ||
+ | " $partOfDomain = $False \n ", | ||
+ | " while ( -not $partOfDomain ) { \n ", | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " } \n ", | ||
+ | " echo \" | ||
+ | " | ||
+ | " | ||
+ | " # add an initial user \n ", | ||
+ | " echo \"Now modify the Active Directory\" | ||
+ | " Try { \n ", | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " } \n ", | ||
+ | " Catch{ \n ", | ||
+ | " | ||
+ | " } \n ", | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " # disable the userdata running task scheduler task, now when we are done \n ", | ||
+ | " echo \"Now Disable the task\" | Out-File c: | ||
+ | " Try { \n ", | ||
+ | " | ||
+ | " } \n ", | ||
+ | " Catch{ \n ", | ||
+ | " | ||
+ | " } \n ", | ||
+ | " | ||
+ | " | ||
+ | " echo \" | ||
+ | " | ||
+ | " </ | ||
+ | " < | ||
+ | } | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | }] | ||
+ | }], | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | }], | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | }] | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | ==== Debugging ==== | ||
+ | |||
+ | === Validate Template === | ||
+ | |||
+ | To validate the local template use the aws command. | ||
+ | The validation includes simple dependency checks too. | ||
+ | |||
+ | < | ||
+ | aws cloudformation validate-template --template-body " | ||
+ | </ | ||
+ | |||
+ | |||
+ | === Initiation Logs of an Instance === | ||
+ | The logs on an Instance are here: /var/log/ | ||
+ | |||
+ | | Human readable logs of the whole instance initiation | / | ||
+ | | Human readable logs of the cfn-init call | / | ||
+ | | Code from AWS:: | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | === Debug the initiation of the instance === | ||
+ | |||
+ | You can repeat the initiation part of the instance by triggering the **cfn-init** script. | ||
+ | < | ||
+ | / | ||
+ | </ | ||
+ | |||
+ | === Debug User Data === | ||
+ | The user data are located under **/ | ||
+ | |||
+ | Here is the complete troubleshooting guide: | ||
+ | https:// | ||
+ | |||
+ | The scripts, created by userData are located under: | ||
+ | **/ | ||
+ | |||
+ | You can try to re run them. | ||
+ | |||
+ | ==== Examples ==== | ||
+ | Here is an example creates a security group, an EC2 Instance and associate the group with the instance. | ||
+ | It has parameters: | ||
+ | - the instance type | ||
+ | - VPC Id | ||
+ | - Server subnet | ||
+ | |||
+ | [[https:// | ||
+ | |||
+ | |||
+ | Here are some examples by AWS | ||
+ | |||
+ | [[https:// | ||
+ | ]] | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | ===== Code Style ===== | ||
+ | |||
+ | Exporting Stack Output Values vs. Using Nested Stacks: | ||
+ | |||
+ | * https:// | ||
+ | * https:// | ||
+ | * https:// | ||
+ | * https:// | ||
+ | |||
+ | |||
+ | Stack A Export: | ||
+ | |||
+ | <sxh js> | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | Stack B Import | ||
+ | <sxh js> | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }] | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | |||
+ | AWS specific parameters: | ||
+ | * https:// | ||
+ | |||
+ | |||
+ | |||
+ | ==== Structure ==== | ||
+ | |||
+ | |||
+ | Example of a custom definition in CLoudFormation. EFS with Fargate in this example | ||
+ | < | ||
+ | |||
+ | CustomTaskDefinition: | ||
+ | Type: ' | ||
+ | Version: ' | ||
+ | Properties: | ||
+ | ServiceToken: | ||
+ | TaskDefinition: | ||
+ | executionRoleArn: | ||
+ | containerDefinitions: | ||
+ | { | ||
+ | name: " | ||
+ | image: !Ref NavvisDockerImage, | ||
+ | memoryReservation: | ||
+ | logConfiguration: | ||
+ | logDriver: " | ||
+ | options: { | ||
+ | awslogs-group: | ||
+ | awslogs-datetime-format: | ||
+ | awslogs-region: | ||
+ | awslogs-stream-prefix: | ||
+ | } | ||
+ | }, | ||
+ | portMappings: | ||
+ | { | ||
+ | hostPort: 8080, | ||
+ | protocol: " | ||
+ | containerPort: | ||
+ | } | ||
+ | ], | ||
+ | command: [], | ||
+ | " | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | { | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | ] | ||
+ | , | ||
+ | | ||
+ | | ||
+ | ] | ||
+ | } | ||
+ | ], | ||
+ | family: " | ||
+ | taskRoleArn: | ||
+ | requiresCompatibilities: | ||
+ | cpu: " | ||
+ | memory: " | ||
+ | networkMode: | ||
+ | volumes: [ | ||
+ | { | ||
+ | name: " | ||
+ | efsVolumeConfiguration: | ||
+ | fileSystemId: | ||
+ | } | ||
+ | }, | ||
+ | ] | ||
+ | } | ||
+ | CustomResourceFunction: | ||
+ | Type: ' | ||
+ | Properties: | ||
+ | Code: | ||
+ | ZipFile: | | ||
+ | const aws = require(' | ||
+ | const response = require(' | ||
+ | const ecs = new aws.ECS({apiVersion: | ||
+ | exports.handler = function(event, | ||
+ | console.log(" | ||
+ | if (event.RequestType === ' | ||
+ | ecs.registerTaskDefinition(event.ResourceProperties.TaskDefinition, | ||
+ | if (err) { | ||
+ | console.error(err); | ||
+ | response.send(event, | ||
+ | } else { | ||
+ | console.log(`Created/ | ||
+ | response.send(event, | ||
+ | } | ||
+ | }) | ||
+ | } else if (event.RequestType === ' | ||
+ | ecs.deregisterTaskDefinition({taskDefinition: | ||
+ | if (err) { | ||
+ | if (err.code === ' | ||
+ | console.log(`Task definition: ${event.PhysicalResourceId} does not exist. Skipping deletion.`) | ||
+ | response.send(event, | ||
+ | } else { | ||
+ | console.error(err) | ||
+ | response.send(event, | ||
+ | } | ||
+ | } else { | ||
+ | console.log(`Removed task definition ${event.PhysicalResourceId}`) | ||
+ | response.send(event, | ||
+ | } | ||
+ | }) | ||
+ | } else { | ||
+ | console.error(`Unsupported request type: ${event.RequestType}`) | ||
+ | response.send(event, | ||
+ | } | ||
+ | } | ||
+ | Handler: ' | ||
+ | MemorySize: 128 | ||
+ | Role: !GetAtt ' | ||
+ | Runtime: ' | ||
+ | Timeout: 30 | ||
+ | CustomResourceRole: | ||
+ | Type: ' | ||
+ | Properties: | ||
+ | AssumeRolePolicyDocument: | ||
+ | Version: ' | ||
+ | Statement: | ||
+ | - Effect: Allow | ||
+ | Principal: | ||
+ | Service: ' | ||
+ | Action: ' | ||
+ | Policies: | ||
+ | - PolicyName: ' | ||
+ | PolicyDocument: | ||
+ | Statement: | ||
+ | - Effect: Allow | ||
+ | Action: | ||
+ | - ' | ||
+ | - ' | ||
+ | Resource: ' | ||
+ | - Effect: Allow | ||
+ | Action: | ||
+ | - ' | ||
+ | - ' | ||
+ | - ' | ||
+ | Resource: ' | ||
+ | - Effect: Allow | ||
+ | Action: | ||
+ | - ' | ||
+ | Resource: ' | ||
+ | CustomLogsGroup: | ||
+ | Type: ' | ||
+ | Properties: | ||
+ | LogGroupName: | ||
+ | RetentionInDays: | ||
+ | |||
+ | |||
+ | NavvisServiceFargate: | ||
+ | Type: ' | ||
+ | Properties: | ||
+ | Cluster: {' | ||
+ | LaunchType: FARGATE | ||
+ | DesiredCount: | ||
+ | PlatformVersion: | ||
+ | DeploymentConfiguration: | ||
+ | MinimumHealthyPercent: | ||
+ | MaximumPercent: | ||
+ | TaskDefinition: | ||
+ | NetworkConfiguration: | ||
+ | AwsvpcConfiguration: | ||
+ | AssignPublicIp: | ||
+ | SecurityGroups: | ||
+ | - {' | ||
+ | Subnets: | ||
+ | - {' | ||
+ | - {' | ||
+ | - {' | ||
+ | LoadBalancers: | ||
+ | - ContainerName: | ||
+ | ContainerPort: | ||
+ | TargetGroupArn: | ||
+ | SchedulingStrategy: | ||
+ | |||
+ | |||
+ | </ | ||
+ | |||