cfn-backup 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/publish-gem.yml +32 -0
- data/.gitignore +11 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +63 -0
- data/LICENSE.txt +21 -0
- data/README.md +120 -0
- data/Rakefile +2 -0
- data/cfn-backup.gemspec +42 -0
- data/components/backup/backup.cfhighlander.rb +17 -0
- data/components/backup/backup.cfndsl.rb +128 -0
- data/exe/cfn-backup +5 -0
- data/lib/cfnbackup.rb +24 -0
- data/lib/cfnbackup/cfhighlander.rb +59 -0
- data/lib/cfnbackup/generate.rb +85 -0
- data/lib/cfnbackup/log.rb +38 -0
- data/lib/cfnbackup/publish.rb +104 -0
- data/lib/cfnbackup/templates/cfnbackup.cfhighlander.rb.tt +33 -0
- data/lib/cfnbackup/utils.rb +44 -0
- data/lib/cfnbackup/version.rb +3 -0
- data/lib/config/global_config.yml +16 -0
- metadata +149 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f9b485ca70d3a19a3c50e4208ed8deea0147308314c053623bbbfd3cdf0fe286
|
4
|
+
data.tar.gz: aeeb9d3a8d2ff548927ebb813a44bc1d10e59ca1a2c7f1ce72a1b628402bdce3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: bca91db583fc25254df3d1d223f094b074aee87dc89cd0d4f3f69c4d7e4fc88b53444f068a2993e559f9808c108a55fded765452e4e34548ef8a08fb458639d0
|
7
|
+
data.tar.gz: ed7a2f7b33c002e0ec9df1fd7aa6cad73e34e916944ab2b67bd73a44a9c00969c9fe95ecf1a333929918d9bbdacd9b9b56aea36bf8e310b9f897f1c6a7695a1e
|
@@ -0,0 +1,32 @@
|
|
1
|
+
name: Publish to RubyGems
|
2
|
+
|
3
|
+
on:
|
4
|
+
pull_request:
|
5
|
+
branches:
|
6
|
+
- master
|
7
|
+
push:
|
8
|
+
branches:
|
9
|
+
- master
|
10
|
+
|
11
|
+
jobs:
|
12
|
+
build:
|
13
|
+
name: Build + Publish
|
14
|
+
runs-on: ubuntu-latest
|
15
|
+
|
16
|
+
steps:
|
17
|
+
- uses: actions/checkout@master
|
18
|
+
- name: Set up Ruby 2.6
|
19
|
+
uses: actions/setup-ruby@v1
|
20
|
+
with:
|
21
|
+
version: 2.6.x
|
22
|
+
|
23
|
+
- name: Publish to RubyGems
|
24
|
+
run: |
|
25
|
+
mkdir -p $HOME/.gem
|
26
|
+
touch $HOME/.gem/credentials
|
27
|
+
chmod 0600 $HOME/.gem/credentials
|
28
|
+
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
29
|
+
gem build *.gemspec
|
30
|
+
gem push *.gem
|
31
|
+
env:
|
32
|
+
GEM_HOST_API_KEY: ${{secrets.RUBYGEMS_AUTH_TOKEN}}
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
cfn-backup (0.1.0)
|
5
|
+
cfhighlander (~> 0.10.3, < 1)
|
6
|
+
cfndsl (~> 0.17.2, < 1)
|
7
|
+
thor (~> 0.20)
|
8
|
+
|
9
|
+
GEM
|
10
|
+
remote: https://rubygems.org/
|
11
|
+
specs:
|
12
|
+
aws-eventstream (1.0.3)
|
13
|
+
aws-partitions (1.260.0)
|
14
|
+
aws-sdk-cloudformation (1.29.0)
|
15
|
+
aws-sdk-core (~> 3, >= 3.71.0)
|
16
|
+
aws-sigv4 (~> 1.1)
|
17
|
+
aws-sdk-core (3.86.0)
|
18
|
+
aws-eventstream (~> 1.0, >= 1.0.2)
|
19
|
+
aws-partitions (~> 1, >= 1.239.0)
|
20
|
+
aws-sigv4 (~> 1.1)
|
21
|
+
jmespath (~> 1.0)
|
22
|
+
aws-sdk-ec2 (1.129.0)
|
23
|
+
aws-sdk-core (~> 3, >= 3.71.0)
|
24
|
+
aws-sigv4 (~> 1.1)
|
25
|
+
aws-sdk-kms (1.27.0)
|
26
|
+
aws-sdk-core (~> 3, >= 3.71.0)
|
27
|
+
aws-sigv4 (~> 1.1)
|
28
|
+
aws-sdk-s3 (1.60.1)
|
29
|
+
aws-sdk-core (~> 3, >= 3.83.0)
|
30
|
+
aws-sdk-kms (~> 1)
|
31
|
+
aws-sigv4 (~> 1.1)
|
32
|
+
aws-sigv4 (1.1.0)
|
33
|
+
aws-eventstream (~> 1.0, >= 1.0.2)
|
34
|
+
cfhighlander (0.10.7)
|
35
|
+
aws-sdk-cloudformation (~> 1, < 2)
|
36
|
+
aws-sdk-core (~> 3, < 4)
|
37
|
+
aws-sdk-ec2 (~> 1, < 2)
|
38
|
+
aws-sdk-s3 (~> 1, < 2)
|
39
|
+
cfndsl (= 0.17.2)
|
40
|
+
duplicate (~> 1.1)
|
41
|
+
git (~> 1.4, < 2)
|
42
|
+
highline (>= 1.7.10, < 1.8)
|
43
|
+
rubyzip (>= 2.0.0, < 3)
|
44
|
+
thor (~> 0.20, < 1)
|
45
|
+
cfndsl (0.17.2)
|
46
|
+
duplicate (1.1.1)
|
47
|
+
git (1.5.0)
|
48
|
+
highline (1.7.10)
|
49
|
+
jmespath (1.4.0)
|
50
|
+
rake (10.5.0)
|
51
|
+
rubyzip (2.0.0)
|
52
|
+
thor (0.20.3)
|
53
|
+
|
54
|
+
PLATFORMS
|
55
|
+
ruby
|
56
|
+
|
57
|
+
DEPENDENCIES
|
58
|
+
bundler (~> 2.0)
|
59
|
+
cfn-backup!
|
60
|
+
rake (~> 10.0)
|
61
|
+
|
62
|
+
BUNDLED WITH
|
63
|
+
2.1.2
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2019 lohgannash
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
# CfnBackup
|
2
|
+
|
3
|
+
Generate & manage configuration for the use of [AWS Backup](https://aws.amazon.com/backup/) to manage backup & retention of your resources. Deployed using CloudFormation.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'cfn-backup'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install cfn-backup
|
20
|
+
|
21
|
+
Setup your [AWS credentials](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-configure.html) by either setting a profile or exporting them as environment variables.
|
22
|
+
|
23
|
+
## Usage
|
24
|
+
|
25
|
+
```bash
|
26
|
+
Commands:
|
27
|
+
cfn-vpn --version, -v # Print the version
|
28
|
+
cfn-vpn generate --stack-name --config # Generate the CloudFormation templates
|
29
|
+
cfn-vpn publish --stack-name --config --source-bucket # Generate & publish the CloudFormation templates to S3
|
30
|
+
cfn-vpn help [COMMAND] # Describe available commands or one specific command
|
31
|
+
```
|
32
|
+
|
33
|
+
Global Options
|
34
|
+
|
35
|
+
```bash
|
36
|
+
p, [--profile=PROFILE] # AWS Profile
|
37
|
+
r, [--region=REGION] # AWS Region
|
38
|
+
# Default: ENV['AWS_REGION']
|
39
|
+
[--verbose], [--no-verbose] # Set log level to debug
|
40
|
+
```
|
41
|
+
|
42
|
+
## How It Works
|
43
|
+
|
44
|
+
Once you have decided upon using the default configuration or you are instead providing your custom configuration, use the generate command to verify the CloudFormation is valid. Then, run the publish command, passing your source bucket to deploy the templates to S3. Ensure you have your AWS credentials and region set up either as environment variables or using the `--profile` flag.
|
45
|
+
|
46
|
+
Once published, you will be given the S3 URL to the master template. Launch this in CloudFormation, and this will create the stack and the nessecary resources.
|
47
|
+
|
48
|
+
This will create the following:
|
49
|
+
|
50
|
+
* Backup Vault - A vault to store the backups in
|
51
|
+
* Backup Plan - A single backup plan containing the rules and resource selection
|
52
|
+
* Backup Rules
|
53
|
+
* Daily Rule
|
54
|
+
* Weekly Rule
|
55
|
+
* Monthly Rule
|
56
|
+
* Yearly Rule
|
57
|
+
* Backup Selection - A single backup selection covering the tag key-value pair specified in your default or custom config
|
58
|
+
|
59
|
+
Simply ensure any resources you wish to be backed up have the tagged applied correctly and they will be included in the next backup job. You can add/remove tags to resources at any time without altering the CloudFormation to ensure new resources will be picked up.
|
60
|
+
|
61
|
+
The following resources are currently supported by AWS Backup:
|
62
|
+
* EFS File Systems
|
63
|
+
* DynamoDB Tables
|
64
|
+
* EBS Volumes (Tagging EC2 Instances is supported, doing this will backup all volumes attached)
|
65
|
+
* RDS Instances
|
66
|
+
* Storage Gateway
|
67
|
+
|
68
|
+
## Custom Configuration
|
69
|
+
|
70
|
+
You can create a custom config file to override the global defaults by providing the path to a YAML file using the `--config` flag.
|
71
|
+
This will perform a deep merge on the global config, meaning you only need to provide the values you want to override in your custom config. The global config file looks like this:
|
72
|
+
|
73
|
+
```yaml
|
74
|
+
# Determines what tag key/value the backup selection will look for on resources
|
75
|
+
tag_key: cfnbackup:enabled
|
76
|
+
tag_value: true
|
77
|
+
|
78
|
+
# The default retention values (in days). Follows the Grandfather-father-son backup
|
79
|
+
daily_retention: 14 # 14 Days
|
80
|
+
weekly_retention: 56 # 8 Weeks
|
81
|
+
monthly_retention: 365 # 12 Months
|
82
|
+
yearly_retention: 3652 # 10 Years
|
83
|
+
|
84
|
+
# The default cron expressions for each rule (Space-seperated, AWS friendly)
|
85
|
+
daily_cron: 0 0 * * ? * # At 12:00 AM UTC, every day
|
86
|
+
weekly_cron: 0 0 ? * 1 * # At 12:00 AM UTC, only on Sunday
|
87
|
+
monthly_cron: 0 0 1 * ? * # At 12:00 AM UTC, on day 1 of the month
|
88
|
+
yearly_cron: 0 0 1 1 ? * # At 12:00 AM UTC, on day 1 of the month, only in January
|
89
|
+
```
|
90
|
+
|
91
|
+
## Custom Rules
|
92
|
+
|
93
|
+
If there are additional rules outside of the default Daily-Weekly-Monthly-Yearly plan, you can define these within your custom configuration file. You must define all properties specified in the example below:
|
94
|
+
|
95
|
+
```yaml
|
96
|
+
custom_rules:
|
97
|
+
hourly: # Alphanumeric name describing the rule
|
98
|
+
cron: 0 0/1 * * ? * # Space-seperated AWS cron expression
|
99
|
+
retention: 3 # Number in days to retain backups taken by this rule
|
100
|
+
tag_key: cfnbackup:custom # A tag key that the service will look for on resources
|
101
|
+
tag_value: hourly # The tag value that must match on the provided tag key
|
102
|
+
```
|
103
|
+
|
104
|
+
This will create a new Backup Plan, Backup Rule and Backup Selection per custom rule defined here. None of the regular configuration defaults or custom configuration defined in the previous section will have any impact on custom rules.
|
105
|
+
|
106
|
+
**NOTE:** AWS Backup has a limitation where the minimum interval between jobs for a backup rule is 60 minutes. This means the lowest interval rule you can currently create will allow you to create *hourly* backups.
|
107
|
+
|
108
|
+
## AWS Backup Pricing
|
109
|
+
|
110
|
+
With AWS Backup, you pay only for the amount of backup storage you use and the amount of backup data you restore in the month. There is no minimum fee and there are no set-up charges. Deploying the CloudFormation is free, and so are the AWS Backup resources.
|
111
|
+
|
112
|
+
Pricing for backup storage and restore differentiates between regions and resources. Please see the [AWS Backup pricing guide](https://aws.amazon.com/backup/pricing/) for a detailed breakdown per region for each resource.
|
113
|
+
|
114
|
+
## Contributing
|
115
|
+
|
116
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/base2services/cfn-backup.
|
117
|
+
|
118
|
+
## License
|
119
|
+
|
120
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/cfn-backup.gemspec
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "cfnbackup/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "cfn-backup"
|
8
|
+
spec.version = CfnBackup::VERSION
|
9
|
+
spec.authors = ["lohgannash"]
|
10
|
+
spec.email = ["lohgannash@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Generates templates and configuration for AWS Backup via CloudFormation}
|
13
|
+
spec.description = %q{Geneate templates and configuration for AWS Backup via CloudFormation}
|
14
|
+
spec.homepage = "https://github.com/base2services/cfn-backup"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
18
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
19
|
+
if spec.respond_to?(:metadata)
|
20
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
21
|
+
spec.metadata["source_code_uri"] = "https://github.com/base2services/cfn-backup"
|
22
|
+
else
|
23
|
+
raise "RubyGems 2.0 or newer is required to protect against " \
|
24
|
+
"public gem pushes."
|
25
|
+
end
|
26
|
+
|
27
|
+
# Specify which files should be added to the gem when it is released.
|
28
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
29
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
30
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
31
|
+
end
|
32
|
+
spec.bindir = "exe"
|
33
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
34
|
+
spec.require_paths = ["lib", "components"]
|
35
|
+
|
36
|
+
spec.add_dependency "thor", "~> 0.20"
|
37
|
+
spec.add_dependency 'cfhighlander', '~> 0.10.3', '<1'
|
38
|
+
spec.add_dependency 'cfndsl', '~> 0.17.2', '<1'
|
39
|
+
|
40
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
41
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
42
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
CfhighlanderTemplate do
|
2
|
+
|
3
|
+
Parameters do
|
4
|
+
ComponentParam 'StackName'
|
5
|
+
ComponentParam 'TagKey'
|
6
|
+
ComponentParam 'TagValue'
|
7
|
+
ComponentParam 'DailyRetention'
|
8
|
+
ComponentParam 'WeeklyRetention'
|
9
|
+
ComponentParam 'MonthlyRetention'
|
10
|
+
ComponentParam 'YearlyRetention'
|
11
|
+
ComponentParam 'DailyCron'
|
12
|
+
ComponentParam 'WeeklyCron'
|
13
|
+
ComponentParam 'MonthlyCron'
|
14
|
+
ComponentParam 'YearlyCron'
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
CloudFormation do
|
2
|
+
|
3
|
+
Backup_BackupVault(:BackupVault) do
|
4
|
+
BackupVaultName FnSub("${StackName}-BackupVault")
|
5
|
+
end
|
6
|
+
|
7
|
+
Backup_BackupPlan(:BackupPlan) do
|
8
|
+
DependsOn :BackupVault
|
9
|
+
BackupPlan {
|
10
|
+
BackupPlanName FnSub("${StackName}-Plan")
|
11
|
+
BackupPlanRule [
|
12
|
+
{
|
13
|
+
RuleName: FnSub("${StackName}-DailyRule"),
|
14
|
+
StartWindowMinutes: 60,
|
15
|
+
TargetBackupVault: FnSub("${StackName}-BackupVault"),
|
16
|
+
ScheduleExpression: FnSub("cron(${DailyCron})"),
|
17
|
+
Lifecycle: {
|
18
|
+
DeleteAfterDays: Ref("DailyRetention")
|
19
|
+
},
|
20
|
+
RecoveryPointTags: {
|
21
|
+
"cfnbackup:type": "daily"
|
22
|
+
}
|
23
|
+
},
|
24
|
+
{
|
25
|
+
RuleName: FnSub("${StackName}-WeeklyRule"),
|
26
|
+
StartWindowMinutes: 60,
|
27
|
+
TargetBackupVault: FnSub("${StackName}-BackupVault"),
|
28
|
+
ScheduleExpression: FnSub("cron(${WeeklyCron})"),
|
29
|
+
Lifecycle: {
|
30
|
+
DeleteAfterDays: Ref("WeeklyRetention")
|
31
|
+
},
|
32
|
+
RecoveryPointTags: {
|
33
|
+
"cfnbackup:type": "weekly"
|
34
|
+
}
|
35
|
+
},
|
36
|
+
{
|
37
|
+
RuleName: FnSub("${StackName}-MonthlyRule"),
|
38
|
+
StartWindowMinutes: 60,
|
39
|
+
TargetBackupVault: FnSub("${StackName}-BackupVault"),
|
40
|
+
ScheduleExpression: FnSub("cron(${MonthlyCron})"),
|
41
|
+
Lifecycle: {
|
42
|
+
DeleteAfterDays: Ref("MonthlyRetention")
|
43
|
+
},
|
44
|
+
RecoveryPointTags: {
|
45
|
+
"cfnbackup:type": "monthly"
|
46
|
+
}
|
47
|
+
},
|
48
|
+
{
|
49
|
+
RuleName: FnSub("${StackName}-YearlyRule"),
|
50
|
+
StartWindowMinutes: 60,
|
51
|
+
TargetBackupVault: FnSub("${StackName}-BackupVault"),
|
52
|
+
ScheduleExpression: FnSub("cron(${YearlyCron})"),
|
53
|
+
Lifecycle: {
|
54
|
+
DeleteAfterDays: Ref("YearlyRetention")
|
55
|
+
},
|
56
|
+
RecoveryPointTags: {
|
57
|
+
"cfnbackup:type": "yearly"
|
58
|
+
}
|
59
|
+
}
|
60
|
+
]
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
Backup_BackupSelection(:BackupSelection) do
|
65
|
+
DependsOn :BackupPlan
|
66
|
+
BackupPlanId FnGetAtt(:BackupPlan, :BackupPlanId)
|
67
|
+
BackupSelection {
|
68
|
+
IamRoleArn FnSub("arn:aws:iam::${AWS::AccountId}:role/service-role/AWSBackupDefaultServiceRole")
|
69
|
+
ListOfTags [
|
70
|
+
{
|
71
|
+
ConditionKey: FnSub("${TagKey}"),
|
72
|
+
ConditionType: "STRINGEQUALS",
|
73
|
+
ConditionValue: FnSub("${TagValue}")
|
74
|
+
}
|
75
|
+
]
|
76
|
+
SelectionName FnSub("${StackName}-Selection")
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
custom_rules = external_parameters.fetch(:custom_rules, [])
|
81
|
+
custom_rules.each do |rule|
|
82
|
+
|
83
|
+
rule_name = rule[0]
|
84
|
+
rule_cron = rule[1]['cron']
|
85
|
+
rule_retention = rule[1]['retention']
|
86
|
+
rule_tag_key = rule[1]['tag_key']
|
87
|
+
rule_tag_value = rule[1]['tag_value']
|
88
|
+
|
89
|
+
Backup_BackupPlan("#{rule_name}BackupPlan") do
|
90
|
+
DependsOn :BackupVault
|
91
|
+
BackupPlan {
|
92
|
+
BackupPlanName FnSub("${StackName}-#{rule_name}Plan")
|
93
|
+
BackupPlanRule [
|
94
|
+
{
|
95
|
+
RuleName: FnSub("${StackName}-#{rule_name}Rule"),
|
96
|
+
StartWindowMinutes: 60,
|
97
|
+
TargetBackupVault: FnSub("${StackName}-BackupVault"),
|
98
|
+
ScheduleExpression: FnSub("cron(#{rule_cron})"),
|
99
|
+
Lifecycle: {
|
100
|
+
DeleteAfterDays: FnSub("#{rule_retention}")
|
101
|
+
},
|
102
|
+
RecoveryPointTags: {
|
103
|
+
"cfnbackup:type": "#{rule_name}"
|
104
|
+
}
|
105
|
+
}
|
106
|
+
]
|
107
|
+
}
|
108
|
+
end
|
109
|
+
|
110
|
+
Backup_BackupSelection("#{rule_name}BackupSelection") do
|
111
|
+
DependsOn "#{rule_name}BackupPlan"
|
112
|
+
BackupPlanId FnGetAtt("#{rule_name}BackupPlan", :BackupPlanId)
|
113
|
+
BackupSelection {
|
114
|
+
IamRoleArn FnSub("arn:aws:iam::${AWS::AccountId}:role/service-role/AWSBackupDefaultServiceRole")
|
115
|
+
ListOfTags [
|
116
|
+
{
|
117
|
+
ConditionKey: FnSub("#{rule_tag_key}"),
|
118
|
+
ConditionType: "STRINGEQUALS",
|
119
|
+
ConditionValue: FnSub("#{rule_tag_value}")
|
120
|
+
}
|
121
|
+
]
|
122
|
+
SelectionName FnSub("${StackName}-#{rule_name}Selection")
|
123
|
+
}
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
data/exe/cfn-backup
ADDED
data/lib/cfnbackup.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require "thor"
|
2
|
+
require "cfnbackup/version"
|
3
|
+
require "cfnbackup/generate"
|
4
|
+
require "cfnbackup/publish"
|
5
|
+
|
6
|
+
module CfnBackup
|
7
|
+
class Cli < Thor
|
8
|
+
|
9
|
+
map %w[--version -v] => :__print_version
|
10
|
+
desc "--version, -v", "Print the version"
|
11
|
+
|
12
|
+
def __print_version
|
13
|
+
puts CfnBackup::VERSION
|
14
|
+
end
|
15
|
+
|
16
|
+
register CfnBackup::Generate, 'generate', 'generate', 'Generates a CloudFormation template'
|
17
|
+
tasks['generate'].options = CfnBackup::Generate.class_options
|
18
|
+
|
19
|
+
register CfnBackup::Publish, 'publish', 'publish', 'Generates, validates and publishes the CloudFormation to S3'
|
20
|
+
tasks['publish'].options = CfnBackup::Publish.class_options
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'cfhighlander.publisher'
|
2
|
+
require 'cfhighlander.factory'
|
3
|
+
require 'cfhighlander.validator'
|
4
|
+
|
5
|
+
require 'cfnbackup/version'
|
6
|
+
|
7
|
+
module CfnBackup
|
8
|
+
class CfHighlander
|
9
|
+
|
10
|
+
def initialize(region, name, config, output_dir)
|
11
|
+
@component_name = name
|
12
|
+
@region = region
|
13
|
+
@config = config
|
14
|
+
@cfn_output_format = 'yaml'
|
15
|
+
@output_dir = output_dir
|
16
|
+
ENV['CFHIGHLANDER_WORKDIR'] = output_dir
|
17
|
+
end
|
18
|
+
|
19
|
+
def render()
|
20
|
+
component = load_component(@component_name)
|
21
|
+
Log.logger.debug("Compiling component, saving generated templates to #{@output_dir}")
|
22
|
+
compiled = compile_component(component)
|
23
|
+
validate_component(component,compiled.cfn_template_paths)
|
24
|
+
cfn_template_paths = compiled.cfn_template_paths
|
25
|
+
return compiled
|
26
|
+
end
|
27
|
+
|
28
|
+
def publish(cf_compiler)
|
29
|
+
publisher = Cfhighlander::Publisher::ComponentPublisher.new(cf_compiler.component, false, @cfn_output_format)
|
30
|
+
Log.logger.debug("Publishing compiled templates to S3")
|
31
|
+
publisher.publishFiles(cf_compiler.cfn_template_paths)
|
32
|
+
Log.logger.debug("Master template URL: #{publisher.getTemplateUrl}")
|
33
|
+
return publisher.getTemplateUrl
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def load_component(component_name)
|
39
|
+
factory = Cfhighlander::Factory::ComponentFactory.new
|
40
|
+
component = factory.loadComponentFromTemplate(component_name)
|
41
|
+
component.config = @config
|
42
|
+
component.version = CfnBackup::VERSION
|
43
|
+
component.load()
|
44
|
+
return component
|
45
|
+
end
|
46
|
+
|
47
|
+
def compile_component(component)
|
48
|
+
component_compiler = Cfhighlander::Compiler::ComponentCompiler.new(component)
|
49
|
+
component_compiler.compileCloudFormation(@cfn_output_format)
|
50
|
+
return component_compiler
|
51
|
+
end
|
52
|
+
|
53
|
+
def validate_component(component,template_paths)
|
54
|
+
component_validator = Cfhighlander::Cloudformation::Validator.new(component)
|
55
|
+
component_validator.validate(template_paths, @cfn_output_format)
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'cfnbackup/log'
|
4
|
+
require 'cfnbackup/utils'
|
5
|
+
require 'cfnbackup/cfhighlander'
|
6
|
+
|
7
|
+
module CfnBackup
|
8
|
+
class Generate < Thor::Group
|
9
|
+
include Thor::Actions
|
10
|
+
include Thor::Shell
|
11
|
+
include CfnBackup::Log
|
12
|
+
|
13
|
+
class_option :profile, aliases: :p, desc: 'AWS Profile'
|
14
|
+
class_option :region, aliases: :r, default: ENV['AWS_REGION'], desc: 'AWS Region'
|
15
|
+
class_option :verbose, desc: 'Enable DEBUG logging', type: :boolean
|
16
|
+
|
17
|
+
class_option :config, desc: 'Provide a path to a config file to override defaults' # Optional
|
18
|
+
class_option :stack_name, desc: 'Override the default stack name for the AWS Backup stack' # Optional
|
19
|
+
|
20
|
+
def self.source_root
|
21
|
+
File.dirname(__FILE__)
|
22
|
+
end
|
23
|
+
|
24
|
+
def set_loglevel
|
25
|
+
Log.logger.level = Logger::DEBUG if @options['verbose']
|
26
|
+
Log.logger.debug("Log level set to DEBUG")
|
27
|
+
end
|
28
|
+
|
29
|
+
# Sets the stack name to be used in template name & resource names. Defaults to cfnbackup if none provided
|
30
|
+
def set_stack_name
|
31
|
+
if @options['stack_name']
|
32
|
+
@stack_name = @options['stack_name']
|
33
|
+
Log.logger.debug("Stack name provided, set to #{@stack_name}")
|
34
|
+
else
|
35
|
+
@stack_name = "cfnbackup"
|
36
|
+
Log.logger.debug("Using default stack name #{@stack_name}")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Creates the build dir based on the stack name
|
41
|
+
def create_build_directory
|
42
|
+
@build_dir = "output/#{@stack_name}"
|
43
|
+
Log.logger.debug("Creating output directory #{@build_dir}")
|
44
|
+
FileUtils.mkdir_p(@build_dir)
|
45
|
+
end
|
46
|
+
|
47
|
+
def initialize_config
|
48
|
+
Log.logger.debug("Initialising config, loading global config file")
|
49
|
+
# Load the global config file (should always be present in the hardcoded path)
|
50
|
+
global_config_path = File.join(File.dirname(__FILE__), '../config/global_config.yml')
|
51
|
+
global_config = YAML.load(File.read(global_config_path))
|
52
|
+
# Check if a custom config file has been provided with the --config flag
|
53
|
+
if @options['config']
|
54
|
+
Log.logger.debug("Custom config file path provided, attempting to load")
|
55
|
+
# Check if the file/path provided is a valid file and attempt to load it using the YAML object.
|
56
|
+
if File.file?(@options['config'])
|
57
|
+
custom_config = YAML.load(File.read(@options['config']))
|
58
|
+
Log.logger.debug("Custom config file loaded, deep merging with global config")
|
59
|
+
# Peform a deep merge on the loaded global config and the custom config
|
60
|
+
@config = CfnBackup::Utils.deep_merge(global_config, custom_config)
|
61
|
+
else
|
62
|
+
abort("Could not find or load file #{@options['config']}")
|
63
|
+
end
|
64
|
+
else
|
65
|
+
# If no custom config was provided no further action is needed
|
66
|
+
Log.logger.debug("No custom config file provided, using all default values")
|
67
|
+
@config = global_config
|
68
|
+
end
|
69
|
+
@config['stack_name'] = @stack_name
|
70
|
+
end
|
71
|
+
|
72
|
+
def generate_cloudformation
|
73
|
+
Log.logger.debug("Populating CfHighlander file from template")
|
74
|
+
# Inject the initalised config list into the text template which will use these to populate parameters
|
75
|
+
template('templates/cfnbackup.cfhighlander.rb.tt', "#{@build_dir}/#{@stack_name}.cfhighlander.rb", @config, force: true)
|
76
|
+
Log.logger.debug("Generating CloudFormation template from #{@build_dir}/#{@stack_name}.cfhighlander.rb")
|
77
|
+
# Initalise the CfHighlander object and run a render, this will compile and validate the component, outputting cloudformation
|
78
|
+
cfhl = CfnBackup::CfHighlander.new(@options['region'], @stack_name, @config, @build_dir)
|
79
|
+
template_path = cfhl.render()
|
80
|
+
Log.logger.debug("CloudFormation template generated and validated")
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module CfnBackup
|
4
|
+
module Log
|
5
|
+
|
6
|
+
def self.colors
|
7
|
+
@colors ||= {
|
8
|
+
ERROR: 31, # Red
|
9
|
+
WARN: 33, # Yellow
|
10
|
+
DEBUG: 32, # Green
|
11
|
+
INFO: 0
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.logger
|
16
|
+
if @logger.nil?
|
17
|
+
@logger = Logger.new(STDOUT)
|
18
|
+
@logger.level = Logger::INFO
|
19
|
+
@logger.formatter = proc do |severity, datetime, progname, msg|
|
20
|
+
"\e[#{colors[severity.to_sym]}m#{severity}: - #{msg}\e[0m\n"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
@logger
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.logger=(logger)
|
27
|
+
@logger = logger
|
28
|
+
end
|
29
|
+
|
30
|
+
levels = %w(debug info warn error fatal)
|
31
|
+
levels.each do |level|
|
32
|
+
define_method("#{level.to_sym}") do |msg|
|
33
|
+
self.logger.send(level, msg)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'cfnbackup/log'
|
4
|
+
require 'cfnbackup/generate'
|
5
|
+
require 'cfnbackup/utils'
|
6
|
+
require 'cfnbackup/cfhighlander'
|
7
|
+
|
8
|
+
module CfnBackup
|
9
|
+
class Publish < Thor::Group
|
10
|
+
include Thor::Actions
|
11
|
+
include Thor::Shell
|
12
|
+
include CfnBackup::Log
|
13
|
+
|
14
|
+
class_option :profile, aliases: :p, desc: 'AWS Profile'
|
15
|
+
class_option :region, aliases: :r, default: ENV['AWS_REGION'], desc: 'AWS Region'
|
16
|
+
class_option :verbose, desc: 'Enable DEBUG logging', type: :boolean
|
17
|
+
|
18
|
+
class_option :config, desc: 'Provide a path to a config file to override defaults' # Optional
|
19
|
+
class_option :stack_name, desc: 'Override the default stack name for the AWS Backup stack' # Optional
|
20
|
+
class_option :source_bucket, desc: 'Source bucket to upload template files to' # Required
|
21
|
+
|
22
|
+
def self.source_root
|
23
|
+
File.dirname(__FILE__)
|
24
|
+
end
|
25
|
+
|
26
|
+
def set_loglevel
|
27
|
+
Log.logger.level = Logger::DEBUG if @options['verbose']
|
28
|
+
Log.logger.debug("Log level set to DEBUG")
|
29
|
+
end
|
30
|
+
|
31
|
+
# Sets the stack name to be used in template name & resource names. Defaults to cfnbackup if none provided
|
32
|
+
def set_stack_name
|
33
|
+
if @options['stack_name']
|
34
|
+
@stack_name = @options['stack_name']
|
35
|
+
Log.logger.debug("Stack name provided, set to #{@stack_name}")
|
36
|
+
else
|
37
|
+
@stack_name = "cfnbackup"
|
38
|
+
Log.logger.debug("Using default stack name #{@stack_name}")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Enforces source bucket to be provided and sets it if it is
|
43
|
+
def set_source_bucket
|
44
|
+
if !@options['source_bucket']
|
45
|
+
Log.logger.debug("No source bucket provided")
|
46
|
+
abort("Set source S3 bucket with --source-bucket flag")
|
47
|
+
else
|
48
|
+
Log.logger.debug("Setting source bucket to #{@options['source_bucket']}")
|
49
|
+
@source_bucket = @options['source_bucket']
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Creates the build dir based on the stack name
|
54
|
+
def create_build_directory
|
55
|
+
@build_dir = "output/#{@stack_name}"
|
56
|
+
Log.logger.debug("Creating output directory #{@build_dir}")
|
57
|
+
FileUtils.mkdir_p(@build_dir)
|
58
|
+
end
|
59
|
+
|
60
|
+
def initialize_config
|
61
|
+
Log.logger.debug("Initialising config, loading global config file")
|
62
|
+
# Load the global config file (should always be present in the hardcoded path)
|
63
|
+
global_config_path = File.join(File.dirname(__FILE__), '../config/global_config.yml')
|
64
|
+
global_config = YAML.load(File.read(global_config_path))
|
65
|
+
# Check if a custom config file has been provided with the --config flag
|
66
|
+
if @options['config']
|
67
|
+
Log.logger.debug("Custom config file path provided, attempting to load")
|
68
|
+
# Check if the file/path provided is a valid file and attempt to load it using the YAML object.
|
69
|
+
if File.file?(@options['config'])
|
70
|
+
custom_config = YAML.load(File.read(@options['config']))
|
71
|
+
Log.logger.debug("Custom config file loaded, deep merging with global config")
|
72
|
+
# Peform a deep merge on the loaded global config and the custom config
|
73
|
+
@config = CfnBackup::Utils.deep_merge(global_config, custom_config)
|
74
|
+
else
|
75
|
+
abort("Could not find or load file #{@options['config']}")
|
76
|
+
end
|
77
|
+
else
|
78
|
+
# If no custom config was provided no further action is needed
|
79
|
+
Log.logger.debug("No custom config file provided, using all default values")
|
80
|
+
@config = global_config
|
81
|
+
end
|
82
|
+
# Load the stack name and source bucket taken from ARGS/Defaults and insert into the final config
|
83
|
+
@config['stack_name'] = @stack_name
|
84
|
+
@config['source_bucket'] = @source_bucket
|
85
|
+
end
|
86
|
+
|
87
|
+
def publish_cloudformation
|
88
|
+
Log.logger.debug("Populating CfHighlander file from template")
|
89
|
+
# Inject the initalised config list into the text template which will use these to populate parameters
|
90
|
+
template('templates/cfnbackup.cfhighlander.rb.tt', "#{@build_dir}/#{@stack_name}.cfhighlander.rb", @config, force: true)
|
91
|
+
Log.logger.debug("Generating CloudFormation template from #{@build_dir}/#{@stack_name}.cfhighlander.rb")
|
92
|
+
# Initalise the CfHighlander object and run a render, this will compile and validate the component, outputting cloudformation
|
93
|
+
cfhl = CfnBackup::CfHighlander.new(@options['region'], @stack_name, nil, @build_dir)
|
94
|
+
compiler = cfhl.render()
|
95
|
+
Log.logger.debug("CloudFormation template generated and validated")
|
96
|
+
# Publishes the compiled cloudformation to S3 using the source bucket provided, outputting the master stack S3 path
|
97
|
+
@template_url = cfhl.publish(compiler)
|
98
|
+
say("\n--------- Master Template URL ---------")
|
99
|
+
say("#{@template_url}")
|
100
|
+
say("---------------------------------------")
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
CfhighlanderTemplate do
|
2
|
+
|
3
|
+
ComponentDistribution "s3://<%= @config['source_bucket'] %>/cfnbackup/cloudformation/<%= @config['stack_name'] %>"
|
4
|
+
|
5
|
+
Parameters do
|
6
|
+
ComponentParam 'StackName', '<%= @config['stack_name'] %>'
|
7
|
+
ComponentParam 'TagKey', '<%= @config['tag_key'] %>'
|
8
|
+
ComponentParam 'TagValue', '<%= @config['tag_value'] %>'
|
9
|
+
ComponentParam 'DailyRetention', '<%= @config['daily_retention'] %>'
|
10
|
+
ComponentParam 'WeeklyRetention', '<%= @config['weekly_retention'] %>'
|
11
|
+
ComponentParam 'MonthlyRetention', '<%= @config['monthly_retention'] %>'
|
12
|
+
ComponentParam 'YearlyRetention', '<%= @config['yearly_retention'] %>'
|
13
|
+
ComponentParam 'DailyCron', '<%= @config['daily_cron'] %>'
|
14
|
+
ComponentParam 'WeeklyCron', '<%= @config['weekly_cron'] %>'
|
15
|
+
ComponentParam 'MonthlyCron', '<%= @config['monthly_cron'] %>'
|
16
|
+
ComponentParam 'YearlyCron', '<%= @config['yearly_cron'] %>'
|
17
|
+
end
|
18
|
+
|
19
|
+
Component template: 'backup', name: 'backup', render: 'inline', config: <%= @config %> do
|
20
|
+
parameter name: 'StackName', value: Ref('StackName')
|
21
|
+
parameter name: 'TagKey', value: Ref('TagKey')
|
22
|
+
parameter name: 'TagValue', value: Ref('TagValue')
|
23
|
+
parameter name: 'DailyRetention', value: Ref('DailyRetention')
|
24
|
+
parameter name: 'WeeklyRetention', value: Ref('WeeklyRetention')
|
25
|
+
parameter name: 'MonthlyRetention', value: Ref('MonthlyRetention')
|
26
|
+
parameter name: 'YearlyRetention', value: Ref('YearlyRetention')
|
27
|
+
parameter name: 'DailyCron', value: Ref('DailyCron')
|
28
|
+
parameter name: 'WeeklyCron', value: Ref('WeeklyCron')
|
29
|
+
parameter name: 'MonthlyCron', value: Ref('MonthlyCron')
|
30
|
+
parameter name: 'YearlyCron', value: Ref('YearlyCron')
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'cfnbackup/log'
|
3
|
+
|
4
|
+
module CfnBackup
|
5
|
+
class Utils
|
6
|
+
include CfnBackup::Log
|
7
|
+
|
8
|
+
def self.deep_merge(global, custom)
|
9
|
+
# Iterate through each key in the custom yaml file
|
10
|
+
custom.each do |k,v|
|
11
|
+
Log.logger.debug("Checking global config for #{k}:#{v}")
|
12
|
+
# Check if the key exists in the global config
|
13
|
+
if global.key? k
|
14
|
+
# Check if the current key is a hash
|
15
|
+
if global[k].class == Hash && custom[k].class == Hash
|
16
|
+
# If it is, we will need to run the deep merge on this again to account for nested config
|
17
|
+
Log.logger.debug("Key #{k} is a Hash present in both templates, running merge recursively")
|
18
|
+
global[k] = deep_merge(global[k], v)
|
19
|
+
else
|
20
|
+
# Once (if any) recursion is complete, override the global value for the current key with the custom value
|
21
|
+
Log.logger.debug("Overriding defaults with key-value pair #{k}:#{v}")
|
22
|
+
global[k] = v
|
23
|
+
end
|
24
|
+
else
|
25
|
+
# If it doesn't exist in the global config, merge it in
|
26
|
+
Log.logger.debug("Key-value pair #{k}:#{v} not found in original config, appending")
|
27
|
+
global[k] = v
|
28
|
+
end
|
29
|
+
end
|
30
|
+
return global
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Hash
|
37
|
+
def without(*keys)
|
38
|
+
dup.without!(*keys)
|
39
|
+
end
|
40
|
+
|
41
|
+
def without!(*keys)
|
42
|
+
reject! { |key| keys.include?(key) }
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
|
2
|
+
# Determines what tag key/value the backup selection will look for on resources
|
3
|
+
tag_key: cfnbackup:enabled
|
4
|
+
tag_value: true
|
5
|
+
|
6
|
+
# The default retention values (in days). Follows the Grandfather-father-son backup
|
7
|
+
daily_retention: 14
|
8
|
+
weekly_retention: 56
|
9
|
+
monthly_retention: 365
|
10
|
+
yearly_retention: 3652
|
11
|
+
|
12
|
+
# The default cron expressions for each rule
|
13
|
+
daily_cron: 0 0 * * ? *
|
14
|
+
weekly_cron: 0 0 ? * 2 *
|
15
|
+
monthly_cron: 0 0 1 * ? *
|
16
|
+
yearly_cron: 0 0 1 1 ? *
|
metadata
ADDED
@@ -0,0 +1,149 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cfn-backup
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- lohgannash
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-01-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: thor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.20'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.20'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: cfhighlander
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.10.3
|
34
|
+
- - "<"
|
35
|
+
- !ruby/object:Gem::Version
|
36
|
+
version: '1'
|
37
|
+
type: :runtime
|
38
|
+
prerelease: false
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - "~>"
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 0.10.3
|
44
|
+
- - "<"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '1'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: cfndsl
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.17.2
|
54
|
+
- - "<"
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '1'
|
57
|
+
type: :runtime
|
58
|
+
prerelease: false
|
59
|
+
version_requirements: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - "~>"
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: 0.17.2
|
64
|
+
- - "<"
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '1'
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: bundler
|
69
|
+
requirement: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - "~>"
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '2.0'
|
74
|
+
type: :development
|
75
|
+
prerelease: false
|
76
|
+
version_requirements: !ruby/object:Gem::Requirement
|
77
|
+
requirements:
|
78
|
+
- - "~>"
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: '2.0'
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: rake
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - "~>"
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '10.0'
|
88
|
+
type: :development
|
89
|
+
prerelease: false
|
90
|
+
version_requirements: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - "~>"
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '10.0'
|
95
|
+
description: Geneate templates and configuration for AWS Backup via CloudFormation
|
96
|
+
email:
|
97
|
+
- lohgannash@gmail.com
|
98
|
+
executables:
|
99
|
+
- cfn-backup
|
100
|
+
extensions: []
|
101
|
+
extra_rdoc_files: []
|
102
|
+
files:
|
103
|
+
- ".github/workflows/publish-gem.yml"
|
104
|
+
- ".gitignore"
|
105
|
+
- Gemfile
|
106
|
+
- Gemfile.lock
|
107
|
+
- LICENSE.txt
|
108
|
+
- README.md
|
109
|
+
- Rakefile
|
110
|
+
- cfn-backup.gemspec
|
111
|
+
- components/backup/backup.cfhighlander.rb
|
112
|
+
- components/backup/backup.cfndsl.rb
|
113
|
+
- exe/cfn-backup
|
114
|
+
- lib/cfnbackup.rb
|
115
|
+
- lib/cfnbackup/cfhighlander.rb
|
116
|
+
- lib/cfnbackup/generate.rb
|
117
|
+
- lib/cfnbackup/log.rb
|
118
|
+
- lib/cfnbackup/publish.rb
|
119
|
+
- lib/cfnbackup/templates/cfnbackup.cfhighlander.rb.tt
|
120
|
+
- lib/cfnbackup/utils.rb
|
121
|
+
- lib/cfnbackup/version.rb
|
122
|
+
- lib/config/global_config.yml
|
123
|
+
homepage: https://github.com/base2services/cfn-backup
|
124
|
+
licenses:
|
125
|
+
- MIT
|
126
|
+
metadata:
|
127
|
+
homepage_uri: https://github.com/base2services/cfn-backup
|
128
|
+
source_code_uri: https://github.com/base2services/cfn-backup
|
129
|
+
post_install_message:
|
130
|
+
rdoc_options: []
|
131
|
+
require_paths:
|
132
|
+
- lib
|
133
|
+
- components
|
134
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
140
|
+
requirements:
|
141
|
+
- - ">="
|
142
|
+
- !ruby/object:Gem::Version
|
143
|
+
version: '0'
|
144
|
+
requirements: []
|
145
|
+
rubygems_version: 3.0.3
|
146
|
+
signing_key:
|
147
|
+
specification_version: 4
|
148
|
+
summary: Generates templates and configuration for AWS Backup via CloudFormation
|
149
|
+
test_files: []
|