├── .github └── img │ └── card.jpeg ├── .gitignore ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── configs └── prod.yml ├── go.mod ├── go.sum ├── main.go ├── package-lock.json ├── policy_example.json └── serverless.yml /.github/img/card.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/msfidelis/aws-turn-off-my-account/12b8a56d2286b09977328ab7be4b9c2c31be5ebd/.github/img/card.jpeg -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | bin 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 13 | 14 | # Runtime data 15 | pids 16 | *.pid 17 | *.seed 18 | *.pid.lock 19 | 20 | # Directory for instrumented libs generated by jscoverage/JSCover 21 | lib-cov 22 | 23 | # Coverage directory used by tools like istanbul 24 | coverage 25 | *.lcov 26 | 27 | # nyc test coverage 28 | .nyc_output 29 | 30 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 31 | .grunt 32 | 33 | # Bower dependency directory (https://bower.io/) 34 | bower_components 35 | 36 | # node-waf configuration 37 | .lock-wscript 38 | 39 | # Compiled binary addons (https://nodejs.org/api/addons.html) 40 | build/Release 41 | 42 | # Dependency directories 43 | node_modules/ 44 | jspm_packages/ 45 | 46 | # TypeScript v1 declaration files 47 | typings/ 48 | 49 | # TypeScript cache 50 | *.tsbuildinfo 51 | 52 | # Optional npm cache directory 53 | .npm 54 | 55 | # Optional eslint cache 56 | .eslintcache 57 | 58 | # Microbundle cache 59 | .rpt2_cache/ 60 | .rts2_cache_cjs/ 61 | .rts2_cache_es/ 62 | .rts2_cache_umd/ 63 | 64 | # Optional REPL history 65 | .node_repl_history 66 | 67 | # Output of 'npm pack' 68 | *.tgz 69 | 70 | # Yarn Integrity file 71 | .yarn-integrity 72 | 73 | # dotenv environment variables file 74 | .env 75 | .env.test 76 | 77 | # parcel-bundler cache (https://parceljs.org/) 78 | .cache 79 | 80 | # Next.js build output 81 | .next 82 | 83 | # Nuxt.js build / generate output 84 | .nuxt 85 | dist 86 | 87 | # Gatsby files 88 | .cache/ 89 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 90 | # https://nextjs.org/blog/next-9-1#public-directory-support 91 | # public 92 | 93 | # vuepress build output 94 | .vuepress/dist 95 | 96 | # Serverless directories 97 | .serverless/ 98 | 99 | # FuseBox cache 100 | .fusebox/ 101 | 102 | # DynamoDB Local files 103 | .dynamodb/ 104 | 105 | # TernJS port file 106 | .tern-port 107 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.13.8-alpine3.10 2 | 3 | WORKDIR /go/src/app 4 | 5 | RUN apk add make git 6 | 7 | RUN go get -u github.com/golang/dep/cmd/dep 8 | 9 | ADD . . 10 | 11 | RUN make build -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Matheus Fidelis 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build clean deploy 2 | 3 | build: 4 | export GO111MODULE="on" 5 | go get -v 6 | env GOOS=linux go build -ldflags="-s -w" -o bin/worker main.go 7 | clean: 8 | rm -rf ./bin 9 | test: 10 | go test 11 | deploy: clean build 12 | serverless deploy --verbose --force 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS, Turn off my Account, please 2 | 3 | Lambda stack to turn off and destroy all resources from your personal AWS Account to avoid billing surprises 4 | 5 | ![Billing Modaf&*@&#](/.github/img/card.jpeg) 6 | 7 | ## Resources Roadmap 8 | 9 | * EC2 :white_check_mark: 10 | * EBS and Snapshots :white_check_mark: 11 | * ALB, ELB, NLB :white_check_mark: 12 | * RDS Instances and Clusters :white_check_mark: 13 | * Elasticache Clusters and Replication Groups :white_check_mark: 14 | * Elastic IP's 15 | * DocumentDB 16 | * NAT Gateways 17 | 18 | ## Installation 19 | 20 | ### Using Serverless Framework 21 | 22 | * Clone this repo 23 | 24 | ```bash 25 | cd $GOPATH/src 26 | git clone https://github.com/msfidelis/aws-turn-off-my-account.git 27 | cd aws-turn-off-my-account 28 | ``` 29 | 30 | * Edit your preferences in `configs/prod.yml` and customize your cron rate on `serverless.yml` 31 | 32 | * Deploy 33 | 34 | ```bash 35 | make deploy 36 | ``` 37 | 38 | ### Using console 39 | 40 | > Guenta ae 41 | 42 | 43 | ### TODO 44 | 45 | * Release ZIP 46 | 47 | * Console setup 48 | 49 | * Cloudformation Setup 50 | 51 | * Tests 52 | 53 | * IAM Closed permissions 54 | 55 | * Logs 56 | -------------------------------------------------------------------------------- /configs/prod.yml: -------------------------------------------------------------------------------- 1 | ENV: prod 2 | REGION: ${self:custom.region} -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module aws-turn-off-my-account 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/aws/aws-lambda-go v1.13.3 7 | github.com/aws/aws-sdk-go v1.27.2 8 | github.com/google/uuid v1.1.1 9 | github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af 10 | ) 11 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 2 | github.com/aws/aws-lambda-go v1.13.3 h1:SuCy7H3NLyp+1Mrfp+m80jcbi9KYWAs9/BXwppwRDzY= 3 | github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= 4 | github.com/aws/aws-sdk-go v1.27.2 h1:yr0Lp4bcrIiP8x4JI9wPG+/t4hjdNJghmYJcKX4wh/g= 5 | github.com/aws/aws-sdk-go v1.27.2/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= 6 | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= 7 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 8 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 9 | github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= 10 | github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 11 | github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= 12 | github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= 13 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 14 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 15 | github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 16 | github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= 17 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 18 | github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= 19 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 20 | github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= 21 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 22 | gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= 23 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 24 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os" 7 | 8 | "github.com/aws/aws-lambda-go/events" 9 | "github.com/aws/aws-lambda-go/lambda" 10 | "github.com/aws/aws-sdk-go/aws" 11 | "github.com/aws/aws-sdk-go/aws/session" 12 | "github.com/aws/aws-sdk-go/service/ec2" 13 | "github.com/aws/aws-sdk-go/service/elbv2" 14 | "github.com/aws/aws-sdk-go/service/rds" 15 | "github.com/aws/aws-sdk-go/service/elasticache" 16 | "github.com/aws/aws-sdk-go/service/sts" 17 | ) 18 | 19 | type Response events.APIGatewayProxyResponse 20 | 21 | var ( 22 | ec2Svc *ec2.EC2 23 | elbSvc *elbv2.ELBV2 24 | rdsSvc *rds.RDS 25 | stsSvc *sts.STS 26 | elcSvc *elasticache.ElastiCache 27 | ) 28 | 29 | func Handler(ctx context.Context) error { 30 | //Handle 31 | ec2Handle() 32 | albHandle() 33 | rdsHandle() 34 | elasticacheHandle() 35 | 36 | return nil 37 | } 38 | 39 | func main() { 40 | 41 | region := os.Getenv("REGION") 42 | 43 | //Prepare 44 | sess, err := getAWSSession(region) 45 | 46 | if err != nil { 47 | panic(err) 48 | } 49 | 50 | // Services 51 | ec2Svc = ec2.New(sess) 52 | elbSvc = elbv2.New(sess) 53 | rdsSvc = rds.New(sess) 54 | stsSvc = sts.New(sess) 55 | elcSvc = elasticache.New(sess) 56 | 57 | // Handle Lambda 58 | lambda.Start(Handler) 59 | // elasticacheHandle() 60 | } 61 | 62 | func ec2Handle() { 63 | fmt.Println("Searching for EC2 Instances") 64 | 65 | instances, err := getEc2Instances() 66 | 67 | if err != nil { 68 | panic(err) 69 | } 70 | 71 | terminateInstances(instances) 72 | 73 | fmt.Println("Searching for Snapshots") 74 | 75 | snapshots, err := getEc2Snapshots() 76 | 77 | if err != nil { 78 | fmt.Println(err) 79 | } 80 | 81 | terminateSnapShots(snapshots) 82 | 83 | fmt.Println("Searching for EBS Volumes") 84 | 85 | volumes, err := getEBSVolumes() 86 | 87 | if err != nil { 88 | panic(err) 89 | } 90 | 91 | terminateEBS(volumes) 92 | } 93 | 94 | func albHandle() { 95 | fmt.Println("Searching for ALB / ELB / NLBs Instances") 96 | 97 | instances, err := getLoadBalancersInstances() 98 | 99 | if err != nil { 100 | panic(err) 101 | } 102 | 103 | terminateLoadBalancers(instances) 104 | } 105 | 106 | func rdsHandle() { 107 | fmt.Println("Searching for RDS Instances") 108 | 109 | instances, err := getRDSInstances() 110 | 111 | if err != nil { 112 | panic(err) 113 | } 114 | 115 | clusters, err := getRDSClusters() 116 | 117 | if err != nil { 118 | panic(err) 119 | } 120 | 121 | terminateRDSInstances(instances) 122 | terminateRDSClusters(clusters) 123 | } 124 | 125 | func elasticacheHandle() { 126 | fmt.Println("Searching for Elasticache Clusters") 127 | 128 | clusters, err := getElasticacheClusters() 129 | 130 | if err != nil { 131 | panic(err) 132 | } 133 | 134 | terminateElasticacheClusters(clusters) 135 | 136 | fmt.Println("Searching for Elasticache Repliocation Groups") 137 | 138 | groups, err := getReplicationGroups() 139 | 140 | if err != nil { 141 | panic(err) 142 | } 143 | 144 | terminateReplicationGroups(groups) 145 | } 146 | 147 | 148 | func getElasticacheClusters() ([]*string, error) { 149 | 150 | var instances []*string 151 | 152 | input := &elasticache.DescribeCacheClustersInput{} 153 | result, err := elcSvc.DescribeCacheClusters(input) 154 | 155 | if err != nil { 156 | return nil, err 157 | } 158 | 159 | for _, cluster := range result.CacheClusters { 160 | if (*cluster.CacheClusterStatus == "available") { 161 | instances = append(instances, cluster.CacheClusterId) 162 | } 163 | } 164 | 165 | return instances, nil 166 | 167 | } 168 | 169 | func getReplicationGroups() ([]*string, error) { 170 | 171 | var instances []*string 172 | 173 | input := &elasticache.DescribeReplicationGroupsInput{} 174 | result, err := elcSvc.DescribeReplicationGroups(input) 175 | 176 | if err != nil { 177 | return nil, err 178 | } 179 | 180 | for _, rpg := range result.ReplicationGroups { 181 | instances = append(instances, rpg.ReplicationGroupId) 182 | } 183 | 184 | return instances, nil 185 | } 186 | 187 | 188 | func getLoadBalancersInstances() ([]*string, error) { 189 | 190 | var instances []*string 191 | 192 | input := &elbv2.DescribeLoadBalancersInput{} 193 | result, err := elbSvc.DescribeLoadBalancers(input) 194 | 195 | if err != nil { 196 | return nil, err 197 | } 198 | 199 | for _, lb := range result.LoadBalancers { 200 | instances = append(instances, lb.LoadBalancerArn) 201 | } 202 | 203 | return instances, nil 204 | 205 | } 206 | 207 | func getEc2Instances() ([]*string, error) { 208 | 209 | var instances []*string 210 | 211 | input := &ec2.DescribeInstancesInput{ 212 | Filters: []*ec2.Filter{ 213 | { 214 | Name: aws.String("instance-state-name"), 215 | Values: []*string{aws.String("running"), aws.String("pending")}, 216 | }, 217 | }, 218 | } 219 | 220 | result, err := ec2Svc.DescribeInstances(input) 221 | 222 | if err != nil { 223 | return nil, err 224 | } 225 | 226 | for _, reservation := range result.Reservations { 227 | for _, instance := range reservation.Instances { 228 | instances = append(instances, instance.InstanceId) 229 | } 230 | } 231 | 232 | return instances, nil 233 | } 234 | 235 | func getEc2Snapshots() ([]*string, error) { 236 | 237 | var snapshots []*string 238 | 239 | account, err := getAWSAccount() 240 | 241 | if err != nil { 242 | return nil, err 243 | } 244 | 245 | filters := []*ec2.Filter{ 246 | { 247 | Name: aws.String("owner-id"), 248 | Values: []*string{ 249 | aws.String(account), 250 | }, 251 | }, 252 | } 253 | 254 | input := &ec2.DescribeSnapshotsInput{Filters: filters} 255 | 256 | result, err := ec2Svc.DescribeSnapshots(input) 257 | 258 | if err != nil { 259 | return nil, err 260 | } 261 | 262 | for _, snapshot := range result.Snapshots { 263 | snapshots = append(snapshots, snapshot.SnapshotId) 264 | } 265 | 266 | return snapshots, nil 267 | 268 | } 269 | 270 | func getEBSVolumes() ([]*string, error) { 271 | var volumes []*string 272 | 273 | result, err := ec2Svc.DescribeVolumes(nil) 274 | 275 | if err != nil { 276 | return nil, err 277 | } 278 | 279 | for _, volume := range result.Volumes { 280 | volumes = append(volumes, volume.VolumeId) 281 | } 282 | 283 | return volumes, nil 284 | } 285 | 286 | func getRDSInstances() ([]*string, error) { 287 | var instances []*string 288 | 289 | result, err := rdsSvc.DescribeDBInstances(nil) 290 | 291 | if err != nil { 292 | return nil, err 293 | } 294 | 295 | for _, rds := range result.DBInstances { 296 | instances = append(instances, rds.DBInstanceIdentifier) 297 | } 298 | 299 | return instances, nil 300 | } 301 | 302 | func getRDSClusters() ([]*string, error) { 303 | var instances []*string 304 | 305 | result, err := rdsSvc.DescribeDBClusters(nil) 306 | 307 | if err != nil { 308 | return nil, err 309 | } 310 | 311 | for _, rds := range result.DBClusters { 312 | instances = append(instances, rds.DBClusterIdentifier) 313 | } 314 | 315 | return instances, nil 316 | } 317 | 318 | func terminateInstances(instances []*string) { 319 | 320 | if len(instances) == 0 { 321 | fmt.Println("No more EC2 instances to destroy") 322 | } else { 323 | 324 | params := &ec2.TerminateInstancesInput{ 325 | InstanceIds: instances, 326 | } 327 | 328 | resp, err := ec2Svc.TerminateInstances(params) 329 | 330 | if err != nil { 331 | fmt.Printf("Failed to terminate instance", err) 332 | } 333 | 334 | for _, ti := range resp.TerminatingInstances { 335 | fmt.Printf("Instance: %s \n\nStatus: %s", *ti.InstanceId, ti.CurrentState.String()) 336 | } 337 | 338 | } 339 | 340 | } 341 | 342 | func terminateSnapShots(snapshots []*string) { 343 | for _, snapshot := range snapshots { 344 | input := &ec2.DeleteSnapshotInput{ 345 | SnapshotId: snapshot, 346 | } 347 | 348 | _, err := ec2Svc.DeleteSnapshot(input) 349 | 350 | if err != nil { 351 | fmt.Printf("Failed to terminate snapshot", err) 352 | } 353 | } 354 | } 355 | 356 | func terminateEBS(volumes []*string) { 357 | for _, volume := range volumes { 358 | fmt.Println(*volume) 359 | 360 | params := &ec2.DeleteVolumeInput{ 361 | VolumeId: volume, 362 | } 363 | 364 | _, err := ec2Svc.DeleteVolume(params) 365 | 366 | if err != nil { 367 | fmt.Printf("Failed to terminate EBS", err) 368 | } 369 | } 370 | } 371 | 372 | func terminateLoadBalancers(instances []*string) { 373 | for _, instance := range instances { 374 | 375 | params := &elbv2.DeleteLoadBalancerInput{ 376 | LoadBalancerArn: instance, 377 | } 378 | 379 | _, err := elbSvc.DeleteLoadBalancer(params) 380 | 381 | if err != nil { 382 | fmt.Printf("Failed to terminate lb", err) 383 | } 384 | 385 | } 386 | } 387 | 388 | func terminateRDSInstances(instances []*string) { 389 | for _, instance := range instances { 390 | 391 | params := &rds.DeleteDBInstanceInput{ 392 | DBInstanceIdentifier: instance, 393 | SkipFinalSnapshot: aws.Bool(true), 394 | } 395 | 396 | _, err := rdsSvc.DeleteDBInstance(params) 397 | 398 | if err != nil { 399 | fmt.Printf("Failed to terminate RDS", err) 400 | } 401 | 402 | } 403 | } 404 | 405 | func terminateRDSClusters(instances []*string) { 406 | for _, instance := range instances { 407 | 408 | params := &rds.DeleteDBClusterInput{ 409 | DBClusterIdentifier: instance, 410 | SkipFinalSnapshot: aws.Bool(true), 411 | } 412 | 413 | _, err := rdsSvc.DeleteDBCluster(params) 414 | 415 | if err != nil { 416 | fmt.Printf("Failed to terminate RDS Cluster", err) 417 | } 418 | 419 | } 420 | } 421 | 422 | func terminateElasticacheClusters(clusters []*string) { 423 | for _, cluster := range clusters { 424 | params := &elasticache.DeleteCacheClusterInput{ 425 | CacheClusterId: cluster, 426 | } 427 | 428 | _, err := elcSvc.DeleteCacheCluster(params) 429 | 430 | if err != nil { 431 | fmt.Printf("Failed to terminate Replication Group", err) 432 | } 433 | 434 | } 435 | } 436 | 437 | func terminateReplicationGroups(groups []*string) { 438 | for _, group := range groups { 439 | params := &elasticache.DeleteReplicationGroupInput{ 440 | ReplicationGroupId: group, 441 | } 442 | 443 | _, err := elcSvc.DeleteReplicationGroup(params) 444 | 445 | if err != nil { 446 | fmt.Printf("Failed to terminate Cache Cluster", err) 447 | } 448 | 449 | } 450 | } 451 | 452 | func getAWSSession(region string) (*session.Session, error) { 453 | awsConfig := &aws.Config{ 454 | Region: aws.String(region), 455 | } 456 | 457 | awsConfig = awsConfig.WithCredentialsChainVerboseErrors(true) 458 | return session.NewSession(awsConfig) 459 | } 460 | 461 | func getAWSAccount() (string, error) { 462 | callerInput := &sts.GetCallerIdentityInput{} 463 | output, err := stsSvc.GetCallerIdentity(callerInput) 464 | 465 | if err != nil { 466 | return "", err 467 | } 468 | 469 | return *output.Account, nil 470 | } 471 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "lockfileVersion": 1 3 | } 4 | -------------------------------------------------------------------------------- /policy_example.json: -------------------------------------------------------------------------------- 1 | { 2 | "Version": "2012-10-17", 3 | "Statement": [ 4 | { 5 | "Action": [ 6 | "logs:CreateLogStream", 7 | "logs:CreateLogGroup", 8 | "logs:PutLogEvents" 9 | ], 10 | "Resource": [ 11 | "*" 12 | ], 13 | "Effect": "Allow" 14 | }, 15 | { 16 | "Action": [ 17 | "ec2:*", 18 | "rds:Describe*", 19 | "rds:List*", 20 | "rds:Delete*", 21 | "sqs:Describe*", 22 | "sqs:List*", 23 | "sqs:Delete*", 24 | "elasticloadbalancing:Describe*", 25 | "elasticloadbalancing:List*", 26 | "elasticloadbalancing:Delete*", 27 | "cloudwatch:*", 28 | "logs:*" 29 | ], 30 | "Resource": "*", 31 | "Effect": "Allow" 32 | } 33 | ] 34 | } -------------------------------------------------------------------------------- /serverless.yml: -------------------------------------------------------------------------------- 1 | service: aws-turn-off-my-account 2 | 3 | provider: 4 | name: aws 5 | runtime: go1.x 6 | stage: ${opt:stage, 'prod'} 7 | timeout: 300 8 | memorySize: 256 9 | versionFunctions: false 10 | 11 | iamRoleStatements: 12 | 13 | - Effect: Allow 14 | Action: 15 | - ec2:* 16 | - rds:Describe* 17 | - rds:List* 18 | - rds:Delete* 19 | - sqs:Describe* 20 | - sqs:List* 21 | - sqs:Delete* 22 | - elasticloadbalancing:Describe* 23 | - elasticloadbalancing:List* 24 | - elasticloadbalancing:Delete* 25 | - cloudwatch:* 26 | - logs:* 27 | - elasticache:* 28 | Resource: "*" 29 | 30 | environment: 31 | ${file(./configs/${self:provider.stage}.yml)} 32 | 33 | package: 34 | exclude: 35 | - ./** 36 | - .git/** 37 | - .vscode/** 38 | - .test/** 39 | include: 40 | - ./bin/** 41 | 42 | custom: 43 | region: ${self:provider.region} 44 | stage: ${opt:stage, self:provider.stage} 45 | prefix: ${self:custom.stage}-${self:service} 46 | 47 | functions: 48 | worker: 49 | handler: bin/worker 50 | events: 51 | - schedule: rate(2 hours) 52 | - schedule: cron(0 12 * * ? *) 53 | --------------------------------------------------------------------------------