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: | ||
| + | |||
| + | |||
| + | </ | ||
| + | |||
