From 050b6a58718c358cfa0bd6ac9cc442bc164a4bdb Mon Sep 17 00:00:00 2001 From: Jason Berlinsky Date: Tue, 26 Mar 2019 23:29:25 -0400 Subject: [PATCH 01/14] Add example tool that automatically cleans up old projects --- .gitignore | 2 + .../project_cleanup/function_source/go.mod | 9 ++ .../project_cleanup/function_source/go.sum | 115 ++++++++++++++++++ .../project_cleanup/function_source/main.go | 103 ++++++++++++++++ examples/project_cleanup/main.tf | 77 ++++++++++++ examples/project_cleanup/outputs.tf | 25 ++++ .../project_cleanup/terraform.tfvars.sample | 5 + examples/project_cleanup/variables.tf | 28 +++++ 8 files changed, 364 insertions(+) create mode 100644 examples/project_cleanup/function_source/go.mod create mode 100644 examples/project_cleanup/function_source/go.sum create mode 100644 examples/project_cleanup/function_source/main.go create mode 100644 examples/project_cleanup/main.tf create mode 100644 examples/project_cleanup/outputs.tf create mode 100644 examples/project_cleanup/terraform.tfvars.sample create mode 100644 examples/project_cleanup/variables.tf diff --git a/.gitignore b/.gitignore index f477bd3..70a1e2b 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ credentials.json *.iml .idea +examples/**/*.zip +examples/**/terraform.tfvars diff --git a/examples/project_cleanup/function_source/go.mod b/examples/project_cleanup/function_source/go.mod new file mode 100644 index 0000000..63fd49d --- /dev/null +++ b/examples/project_cleanup/function_source/go.mod @@ -0,0 +1,9 @@ +module github.com/terraform-google-modules/project-cleaner + +go 1.11 + +require ( + golang.org/x/net v0.0.0-20190318221613-d196dffd7c2b + golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 + google.golang.org/api v0.1.0 +) diff --git a/examples/project_cleanup/function_source/go.sum b/examples/project_cleanup/function_source/go.sum new file mode 100644 index 0000000..c24552f --- /dev/null +++ b/examples/project_cleanup/function_source/go.sum @@ -0,0 +1,115 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= +git.apache.org/thrift.git v0.12.0/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= +github.com/grpc-ecosystem/grpc-gateway v1.6.2/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/openzipkin/zipkin-go v0.1.3/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= +go.opencensus.io v0.19.1/go.mod h1:gug0GbSHa8Pafr0d2urOSgoXHZ6x/RUlaiT0d9pqb4A= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20181217174547-8f45f776aaf1/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190318221613-d196dffd7c2b h1:ZWpVMTsK0ey5WJCu+vVdfMldWq7/ezaOcjnKWIHWVkE= +golang.org/x/net v0.0.0-20190318221613-d196dffd7c2b/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181218192612-074acd46bca6/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181219222714-6e267b5cc78e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.0.0-20181220000619-583d854617af/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.1.0 h1:K6z2u68e86TPdSdefXdzvXgR1zEMa+459vBSfWYAZkI= +google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= +google.golang.org/api v0.2.0 h1:B5VXkdjt7K2Gm6fGBC9C9a1OAKJDT95cTqwet+2zib0= +google.golang.org/api v0.2.0/go.mod h1:IfRCZScioGtypHNTlz3gFk67J8uePVW7uDTBzXuIkhU= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= +google.golang.org/genproto v0.0.0-20181219182458-5a97ab628bfb/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20180920025451-e3ad64cb4ed3/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/examples/project_cleanup/function_source/main.go b/examples/project_cleanup/function_source/main.go new file mode 100644 index 0000000..348f4bf --- /dev/null +++ b/examples/project_cleanup/function_source/main.go @@ -0,0 +1,103 @@ +// Copyright 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package project_cleaner + +import ( + "fmt" + "log" + "os" + "strconv" + "time" + + "golang.org/x/net/context" + "golang.org/x/oauth2/google" + "google.golang.org/api/cloudresourcemanager/v1" +) + +var ( + logger = log.New(os.Stdout, "", 0) +) + +// PubSubMessage wraps the message sent to the background Cloud Function by GCP PubSub. +type PubSubMessage struct { + Data []byte `json:"data"` +} + +// CleanUpProjects is the entry point of the scheduled function. +func CleanUpProjects(ctx context.Context, m PubSubMessage) error { + targetTag := os.Getenv("TARGET_TAG_NAME") + targetValue := os.Getenv("TARGET_TAG_VALUE") + maxAgeInHoursStr := os.Getenv("MAX_PROJECT_AGE_HOURS") + maxAgeInHours, err := strconv.ParseInt(maxAgeInHoursStr, 10, 0) + if err != nil { + logger.Fatal(fmt.Sprintf("Could not convert %s to integer", maxAgeInHoursStr)) + return err + } + logger.Println(fmt.Sprintf("Launching project cleanup for projects older than %d hours, with tag %s=%s", maxAgeInHours, targetTag, targetValue)) + err = deleteProjectsMatchingTag(ctx, targetTag, targetValue, maxAgeInHours) + if err != nil { + logger.Fatal(err) + } + return err +} + +func deleteProjectsMatchingTag(ctx context.Context, key string, value string, acceptableAgeInHours int64) error { + logger.Println("Initializing Google client") + c, err := google.DefaultClient(ctx, cloudresourcemanager.CloudPlatformScope) + if err != nil { + return err + } + + cloudResourceManagerService, err := cloudresourcemanager.New(c) + if err != nil { + return err + } + + resourceCreationCutoff := tooOldTime(int64(acceptableAgeInHours * 60 * 60)) + + logger.Println("Looking through projects") + req := cloudResourceManagerService.Projects.List() + if err := req.Pages(ctx, func(page *cloudresourcemanager.ListProjectsResponse) error { + for _, project := range page.Projects { + if val, ok := project.Labels[key]; ok && val == value { + logger.Println(fmt.Sprintf("Considering project %s...", project.ProjectId)) + if project.LifecycleState == "ACTIVE" { + projectCreatedAt, err := time.Parse(time.RFC3339, project.CreateTime) + if err != nil { + return err + } + if projectCreatedAt.Before(resourceCreationCutoff) { + logger.Println(fmt.Sprintf("Project %s was created at %s, is active, and is older than %d hours. Deleting.", project.ProjectId, project.CreateTime, acceptableAgeInHours)) + _, err = cloudResourceManagerService.Projects.Delete(project.ProjectId).Do() + if err != nil { + logger.Fatal(err) + return err + } + logger.Println(fmt.Sprintf("Requested deletion of project %s.", project.ProjectId)) + } + } + } + } + return nil + }); err != nil { + return err + } + logger.Println("Considered all projects") + return nil +} + +func tooOldTime(i int64) time.Time { + return time.Unix(time.Now().Unix()-i, 0) +} diff --git a/examples/project_cleanup/main.tf b/examples/project_cleanup/main.tf new file mode 100644 index 0000000..9a29dff --- /dev/null +++ b/examples/project_cleanup/main.tf @@ -0,0 +1,77 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +provider "google-beta" { + version = "~> 2.1" + project = "${var.project_id}" + region = "${var.region}" +} + +resource "google_project_service" "cloudfunctions" { + project = "${var.project_id}" + service = "cloudfunctions.googleapis.com" + disable_dependent_services = true +} + +resource "google_project_service" "cloudscheduler" { + project = "${google_project_service.cloudfunctions.project}" + service = "cloudscheduler.googleapis.com" + disable_dependent_services = true +} + +resource "google_project_service" "cloudresourcemanager" { + project = "${google_project_service.cloudscheduler.project}" + service = "cloudresourcemanager.googleapis.com" + disable_dependent_services = true +} + +resource "google_service_account" "project_cleaner_function" { + project = "${var.project_id}" + account_id = "project-cleaner-function" + display_name = "Project Cleaner Function" +} + +resource "google_organization_iam_member" "project_owner" { + org_id = "${var.organization_id}" + role = "roles/owner" + member = "serviceAccount:${google_service_account.project_cleaner_function.email}" +} + +module "scheduled_project_cleaner" { + providers = { + google = "google-beta" + } + + source = "../../" + project_id = "${google_project_service.cloudresourcemanager.project}" + job_name = "project-cleaner" + job_schedule = "*/5 * * * *" + function_entry_point = "CleanUpProjects" + function_source_directory = "${path.module}/function_source" + function_name = "old-project-cleaner" + region = "${var.region}" + topic_name = "pubsub_scheduled_project_cleaner" + function_available_memory_mb = "128" + function_description = "Clean up GCP projects older than 6 hours matching particular tags" + function_runtime = "go111" + function_service_account_email = "${google_service_account.project_cleaner_function.email}" + + function_environment_variables = { + TARGET_TAG_NAME = "cft-ephemeral" + TARGET_TAG_VALUE = "true" + MAX_PROJECT_AGE_HOURS = "6" + } +} diff --git a/examples/project_cleanup/outputs.tf b/examples/project_cleanup/outputs.tf new file mode 100644 index 0000000..290b9a2 --- /dev/null +++ b/examples/project_cleanup/outputs.tf @@ -0,0 +1,25 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +output "name" { + value = "${module.scheduled_project_cleaner.name}" + description = "The name of the job created" +} + +output "project_id" { + value = "${var.project_id}" + description = "The project ID" +} diff --git a/examples/project_cleanup/terraform.tfvars.sample b/examples/project_cleanup/terraform.tfvars.sample new file mode 100644 index 0000000..5ae9d8f --- /dev/null +++ b/examples/project_cleanup/terraform.tfvars.sample @@ -0,0 +1,5 @@ +organization_id = "" + +project_id = "" + +region = "" diff --git a/examples/project_cleanup/variables.tf b/examples/project_cleanup/variables.tf new file mode 100644 index 0000000..a9bfb4c --- /dev/null +++ b/examples/project_cleanup/variables.tf @@ -0,0 +1,28 @@ +/** + * Copyright 2019 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +variable "organization_id" { + description = "The organization ID whose projects to clean up" +} + +variable "project_id" { + description = "The project ID to host the scheduled function in" +} + +variable "region" { + description = "The region the project is in (App Engine specific)" + default = "us-central1" +} From 0ea513a2c869d8c4adab5a0bcc0e9070f9ed32fd Mon Sep 17 00:00:00 2001 From: Jason Berlinsky Date: Tue, 26 Mar 2019 23:42:43 -0400 Subject: [PATCH 02/14] Document project cleanup utility --- examples/project_cleanup/README.md | 25 +++++++++++++++++ .../project_cleanup/function_source/README.md | 20 +++++++++++++ .../project_cleanup/function_source/main.go | 28 ++++++++++--------- 3 files changed, 60 insertions(+), 13 deletions(-) create mode 100644 examples/project_cleanup/README.md create mode 100644 examples/project_cleanup/function_source/README.md diff --git a/examples/project_cleanup/README.md b/examples/project_cleanup/README.md new file mode 100644 index 0000000..ffbd599 --- /dev/null +++ b/examples/project_cleanup/README.md @@ -0,0 +1,25 @@ +# Old Project Cleanup Utility Module + +This example module schedules a job to clean up GCP projects older than a specified length of time, that match a particular key-value pair. This job runs every 5 minutes via Google Cloud Scheduled Functions. Please see the [utility's readme](./function_source/README.md) for more information as to its operation and configuration. + +Running this module requires an App Engine app in the specified project/region, which is not handled by this example. More information is in the [root readme](../../README.md#app-engine). + +[^]: (autogen_docs_start) + + +## Inputs + +| Name | Description | Type | Default | Required | +|------|-------------|:----:|:-----:|:-----:| +| organization_id | The organization ID whose projects to clean up | string | - | yes | +| project_id | The project ID to host the scheduled function in | string | - | yes | +| region | The region the project is in (App Engine specific) | string | `us-central1` | no | + +## Outputs + +| Name | Description | +|------|-------------| +| name | The name of the job created | +| project_id | The project ID | + +[^]: (autogen_docs_end) diff --git a/examples/project_cleanup/function_source/README.md b/examples/project_cleanup/function_source/README.md new file mode 100644 index 0000000..80c26a6 --- /dev/null +++ b/examples/project_cleanup/function_source/README.md @@ -0,0 +1,20 @@ +# Project Cleanup Utility + +This is a simple utility that scans a GCP organization for projects matching certain criteria, and enqueues such projects for deletion. Currently supported criteria are the combination of: + +- **Age:** Only projects older than the configured age, in hours, will be marked for deletion. +- **Key-Value Pair:** Only projects whose labels contain the provided key-value pair will be marked for deletion. + +## Environment Configuration + +The following environment variables may be specified to configure the cleanup utility: + +| Name | Description | Type | Default | Required | +|------|-------------|:----:|:-----:|:-----:| +| `TARGET_TAG_NAME` | The tag name to match on for identifying projects to delete | string | n/a | yes | +| `TARGET_TAG_VALUE` | The tag value to match on for identifying projects to delete | string | n/a | yes | +| `MAX_PROJECT_AGE_HOURS` | The project age, in hours, at which point deletion should be considered | integer | n/a | yes | + +## Required Permissions + +This Cloud Function must be run as a Service Account with the `owner` role at an organization level. diff --git a/examples/project_cleanup/function_source/main.go b/examples/project_cleanup/function_source/main.go index 348f4bf..64102d0 100644 --- a/examples/project_cleanup/function_source/main.go +++ b/examples/project_cleanup/function_source/main.go @@ -1,16 +1,18 @@ -// Copyright 2019 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* +Copyright 2019 Google LLC + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ package project_cleaner From 5582afa0c8f167b98fa23ec40fe7e14bef7931e2 Mon Sep 17 00:00:00 2001 From: Jason Berlinsky Date: Fri, 10 May 2019 17:12:16 -0400 Subject: [PATCH 03/14] Make project_cleanup example a submodule --- {examples => submodules}/project_cleanup/README.md | 0 .../project_cleanup/function_source/README.md | 0 .../project_cleanup/function_source/go.mod | 0 .../project_cleanup/function_source/go.sum | 0 .../project_cleanup/function_source/main.go | 0 {examples => submodules}/project_cleanup/main.tf | 0 {examples => submodules}/project_cleanup/outputs.tf | 0 submodules/project_cleanup/terraform.tfvars | 5 +++++ .../project_cleanup/terraform.tfvars.sample | 0 {examples => submodules}/project_cleanup/variables.tf | 0 10 files changed, 5 insertions(+) rename {examples => submodules}/project_cleanup/README.md (100%) rename {examples => submodules}/project_cleanup/function_source/README.md (100%) rename {examples => submodules}/project_cleanup/function_source/go.mod (100%) rename {examples => submodules}/project_cleanup/function_source/go.sum (100%) rename {examples => submodules}/project_cleanup/function_source/main.go (100%) rename {examples => submodules}/project_cleanup/main.tf (100%) rename {examples => submodules}/project_cleanup/outputs.tf (100%) create mode 100644 submodules/project_cleanup/terraform.tfvars rename {examples => submodules}/project_cleanup/terraform.tfvars.sample (100%) rename {examples => submodules}/project_cleanup/variables.tf (100%) diff --git a/examples/project_cleanup/README.md b/submodules/project_cleanup/README.md similarity index 100% rename from examples/project_cleanup/README.md rename to submodules/project_cleanup/README.md diff --git a/examples/project_cleanup/function_source/README.md b/submodules/project_cleanup/function_source/README.md similarity index 100% rename from examples/project_cleanup/function_source/README.md rename to submodules/project_cleanup/function_source/README.md diff --git a/examples/project_cleanup/function_source/go.mod b/submodules/project_cleanup/function_source/go.mod similarity index 100% rename from examples/project_cleanup/function_source/go.mod rename to submodules/project_cleanup/function_source/go.mod diff --git a/examples/project_cleanup/function_source/go.sum b/submodules/project_cleanup/function_source/go.sum similarity index 100% rename from examples/project_cleanup/function_source/go.sum rename to submodules/project_cleanup/function_source/go.sum diff --git a/examples/project_cleanup/function_source/main.go b/submodules/project_cleanup/function_source/main.go similarity index 100% rename from examples/project_cleanup/function_source/main.go rename to submodules/project_cleanup/function_source/main.go diff --git a/examples/project_cleanup/main.tf b/submodules/project_cleanup/main.tf similarity index 100% rename from examples/project_cleanup/main.tf rename to submodules/project_cleanup/main.tf diff --git a/examples/project_cleanup/outputs.tf b/submodules/project_cleanup/outputs.tf similarity index 100% rename from examples/project_cleanup/outputs.tf rename to submodules/project_cleanup/outputs.tf diff --git a/submodules/project_cleanup/terraform.tfvars b/submodules/project_cleanup/terraform.tfvars new file mode 100644 index 0000000..9f28e11 --- /dev/null +++ b/submodules/project_cleanup/terraform.tfvars @@ -0,0 +1,5 @@ +organization_id = "826592752744" + +project_id = "berlinsky-pf-gke-fixture-f466" + +region = "us-central1" diff --git a/examples/project_cleanup/terraform.tfvars.sample b/submodules/project_cleanup/terraform.tfvars.sample similarity index 100% rename from examples/project_cleanup/terraform.tfvars.sample rename to submodules/project_cleanup/terraform.tfvars.sample diff --git a/examples/project_cleanup/variables.tf b/submodules/project_cleanup/variables.tf similarity index 100% rename from examples/project_cleanup/variables.tf rename to submodules/project_cleanup/variables.tf From 879316cee32764b86e913b0a5e28c54a843df67d Mon Sep 17 00:00:00 2001 From: Jason Berlinsky Date: Fri, 10 May 2019 17:13:21 -0400 Subject: [PATCH 04/14] Clarify that both conditions must be met to delete projects --- submodules/project_cleanup/function_source/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/submodules/project_cleanup/function_source/README.md b/submodules/project_cleanup/function_source/README.md index 80c26a6..4941c31 100644 --- a/submodules/project_cleanup/function_source/README.md +++ b/submodules/project_cleanup/function_source/README.md @@ -5,6 +5,8 @@ This is a simple utility that scans a GCP organization for projects matching cer - **Age:** Only projects older than the configured age, in hours, will be marked for deletion. - **Key-Value Pair:** Only projects whose labels contain the provided key-value pair will be marked for deletion. +Both of these criteria must be met for a project to be deleted. + ## Environment Configuration The following environment variables may be specified to configure the cleanup utility: From c8a7c6ed3975fd071d7cb28b07975b6886bd3b66 Mon Sep 17 00:00:00 2001 From: Jason Berlinsky Date: Tue, 14 May 2019 23:30:30 -0400 Subject: [PATCH 05/14] Clean up nested if statements --- .../project_cleanup/function_source/main.go | 44 ++++++++++++------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/submodules/project_cleanup/function_source/main.go b/submodules/project_cleanup/function_source/main.go index 64102d0..9471ffc 100644 --- a/submodules/project_cleanup/function_source/main.go +++ b/submodules/project_cleanup/function_source/main.go @@ -73,24 +73,34 @@ func deleteProjectsMatchingTag(ctx context.Context, key string, value string, ac req := cloudResourceManagerService.Projects.List() if err := req.Pages(ctx, func(page *cloudresourcemanager.ListProjectsResponse) error { for _, project := range page.Projects { - if val, ok := project.Labels[key]; ok && val == value { - logger.Println(fmt.Sprintf("Considering project %s...", project.ProjectId)) - if project.LifecycleState == "ACTIVE" { - projectCreatedAt, err := time.Parse(time.RFC3339, project.CreateTime) - if err != nil { - return err - } - if projectCreatedAt.Before(resourceCreationCutoff) { - logger.Println(fmt.Sprintf("Project %s was created at %s, is active, and is older than %d hours. Deleting.", project.ProjectId, project.CreateTime, acceptableAgeInHours)) - _, err = cloudResourceManagerService.Projects.Delete(project.ProjectId).Do() - if err != nil { - logger.Fatal(err) - return err - } - logger.Println(fmt.Sprintf("Requested deletion of project %s.", project.ProjectId)) - } - } + if val, ok := project.Labels[key]; !(ok && val == value) { + logger.Println(fmt.Sprintf("Rejecting project %s because of missing label", project.ProjectId)) + continue } + + if project.LifecycleState != "ACTIVE" { + logger.Println(fmt.Sprintf("Rejecting project %s because lifecycle state is not ACTIVE", project.ProjectId)) + continue + } + + projectCreatedAt, err := time.Parse(time.RFC3339, project.CreateTime) + if err != nil { + return err + } + + if !projectCreatedAt.Before(resourceCreationCutoff) { + logger.Println(fmt.Sprintf("Rejecting project %s because it was created too recently", project.ProjectId)) + continue + } + + logger.Println(fmt.Sprintf("Project %s was created at %s, is active, and is older than %d hours. Deleting.", project.ProjectId, project.CreateTime, acceptableAgeInHours)) + + _, err = cloudResourceManagerService.Projects.Delete(project.ProjectId).Do() + if err != nil { + logger.Fatal(err) + return err + } + logger.Println(fmt.Sprintf("Requested deletion of project %s.", project.ProjectId)) } return nil }); err != nil { From 081171c1cabd48d92926f26420a5be1c5ec163d7 Mon Sep 17 00:00:00 2001 From: Jason Berlinsky Date: Tue, 14 May 2019 23:30:49 -0400 Subject: [PATCH 06/14] Do not disable project services when destroyed via Terraform --- submodules/project_cleanup/main.tf | 3 +++ 1 file changed, 3 insertions(+) diff --git a/submodules/project_cleanup/main.tf b/submodules/project_cleanup/main.tf index 9a29dff..c3bbc77 100644 --- a/submodules/project_cleanup/main.tf +++ b/submodules/project_cleanup/main.tf @@ -24,18 +24,21 @@ resource "google_project_service" "cloudfunctions" { project = "${var.project_id}" service = "cloudfunctions.googleapis.com" disable_dependent_services = true + disable_on_destroy = false } resource "google_project_service" "cloudscheduler" { project = "${google_project_service.cloudfunctions.project}" service = "cloudscheduler.googleapis.com" disable_dependent_services = true + disable_on_destroy = false } resource "google_project_service" "cloudresourcemanager" { project = "${google_project_service.cloudscheduler.project}" service = "cloudresourcemanager.googleapis.com" disable_dependent_services = true + disable_on_destroy = false } resource "google_service_account" "project_cleaner_function" { From 3ecc7faf3f1945b1d9e4b30150bb885d5ce4edca Mon Sep 17 00:00:00 2001 From: Jason Berlinsky Date: Tue, 14 May 2019 23:30:59 -0400 Subject: [PATCH 07/14] Clarify required role for SA --- submodules/project_cleanup/function_source/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/submodules/project_cleanup/function_source/README.md b/submodules/project_cleanup/function_source/README.md index 4941c31..6149834 100644 --- a/submodules/project_cleanup/function_source/README.md +++ b/submodules/project_cleanup/function_source/README.md @@ -19,4 +19,4 @@ The following environment variables may be specified to configure the cleanup ut ## Required Permissions -This Cloud Function must be run as a Service Account with the `owner` role at an organization level. +This Cloud Function must be run as a Service Account with the `Organization Administrator` role. From fadb3469503e98bb235be16466f2bc8b554c0e4a Mon Sep 17 00:00:00 2001 From: Jason Berlinsky Date: Sat, 1 Jun 2019 10:42:08 -0400 Subject: [PATCH 08/14] Clean up README --- submodules/project_cleanup/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/submodules/project_cleanup/README.md b/submodules/project_cleanup/README.md index ffbd599..95534af 100644 --- a/submodules/project_cleanup/README.md +++ b/submodules/project_cleanup/README.md @@ -1,8 +1,8 @@ # Old Project Cleanup Utility Module -This example module schedules a job to clean up GCP projects older than a specified length of time, that match a particular key-value pair. This job runs every 5 minutes via Google Cloud Scheduled Functions. Please see the [utility's readme](./function_source/README.md) for more information as to its operation and configuration. +This module schedules a job to clean up GCP projects older than a specified length of time, that match a particular key-value pair. This job runs every 5 minutes via Google Cloud Scheduled Functions. Please see the [utility's readme](./function_source/README.md) for more information as to its operation and configuration. -Running this module requires an App Engine app in the specified project/region, which is not handled by this example. More information is in the [root readme](../../README.md#app-engine). +Running this module requires an App Engine app in the specified project/region. More information is in the [root readme](../../README.md#app-engine). [^]: (autogen_docs_start) From a50d8429b59afa6eae45a07611a653d19d6234a5 Mon Sep 17 00:00:00 2001 From: Jason Berlinsky Date: Sat, 1 Jun 2019 10:46:52 -0400 Subject: [PATCH 09/14] Remove errant tfvars --- submodules/project_cleanup/terraform.tfvars | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 submodules/project_cleanup/terraform.tfvars diff --git a/submodules/project_cleanup/terraform.tfvars b/submodules/project_cleanup/terraform.tfvars deleted file mode 100644 index 9f28e11..0000000 --- a/submodules/project_cleanup/terraform.tfvars +++ /dev/null @@ -1,5 +0,0 @@ -organization_id = "826592752744" - -project_id = "berlinsky-pf-gke-fixture-f466" - -region = "us-central1" From a0479a9d0e9fecc3e068e264c7d970b1ee1dc258 Mon Sep 17 00:00:00 2001 From: Jason Berlinsky Date: Sat, 1 Jun 2019 10:47:12 -0400 Subject: [PATCH 10/14] Gitignore tfvars files --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 70a1e2b..8b1312e 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ credentials.json examples/**/*.zip examples/**/terraform.tfvars +modules/**/terraform.tfvars +submodules/**/terraform.tfvars From bbf3295c728fee4e7f80cc7255e1acbecf081910 Mon Sep 17 00:00:00 2001 From: Jason Berlinsky Date: Sat, 1 Jun 2019 10:47:44 -0400 Subject: [PATCH 11/14] Remove default from region variable --- submodules/project_cleanup/variables.tf | 1 - 1 file changed, 1 deletion(-) diff --git a/submodules/project_cleanup/variables.tf b/submodules/project_cleanup/variables.tf index a9bfb4c..34374d7 100644 --- a/submodules/project_cleanup/variables.tf +++ b/submodules/project_cleanup/variables.tf @@ -24,5 +24,4 @@ variable "project_id" { variable "region" { description = "The region the project is in (App Engine specific)" - default = "us-central1" } From f1a96560a803e6518913cd4d564150072e2681e3 Mon Sep 17 00:00:00 2001 From: Jason Berlinsky Date: Sat, 1 Jun 2019 11:01:10 -0400 Subject: [PATCH 12/14] Extract filter parameters to variables --- submodules/project_cleanup/main.tf | 8 ++++---- submodules/project_cleanup/variables.tf | 15 +++++++++++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/submodules/project_cleanup/main.tf b/submodules/project_cleanup/main.tf index c3bbc77..b0efc39 100644 --- a/submodules/project_cleanup/main.tf +++ b/submodules/project_cleanup/main.tf @@ -68,13 +68,13 @@ module "scheduled_project_cleaner" { region = "${var.region}" topic_name = "pubsub_scheduled_project_cleaner" function_available_memory_mb = "128" - function_description = "Clean up GCP projects older than 6 hours matching particular tags" + function_description = "Clean up GCP projects older than ${var.max_project_age_in_hours} hours matching particular tags" function_runtime = "go111" function_service_account_email = "${google_service_account.project_cleaner_function.email}" function_environment_variables = { - TARGET_TAG_NAME = "cft-ephemeral" - TARGET_TAG_VALUE = "true" - MAX_PROJECT_AGE_HOURS = "6" + TARGET_TAG_NAME = "${var.target_tag_name}" + TARGET_TAG_VALUE = "${var.target_tag_value}" + MAX_PROJECT_AGE_HOURS = "${var.max_project_age_in_hours}" } } diff --git a/submodules/project_cleanup/variables.tf b/submodules/project_cleanup/variables.tf index 34374d7..6ef3273 100644 --- a/submodules/project_cleanup/variables.tf +++ b/submodules/project_cleanup/variables.tf @@ -25,3 +25,18 @@ variable "project_id" { variable "region" { description = "The region the project is in (App Engine specific)" } + +variable "target_tag_name" { + description = "The name of a tag to filter GCP projects on for consideration by the cleanup utility" + default = "cft-ephemeral" +} + +variable "target_tag_value" { + description = "The value of a tag to filter GCP projects on for consideration by the cleanup utility" + default = "true" +} + +variable "max_project_age_in_hours" { + description = "The maximum number of hours that a GCP project, selected by `target_tag_name` and `target_tag_value`, can exist" + default = "6" +} From d659bc0c00c4d6f668c8761ce2174d77b072b844 Mon Sep 17 00:00:00 2001 From: Jason Berlinsky Date: Sat, 1 Jun 2019 11:02:00 -0400 Subject: [PATCH 13/14] Rename submodules directory to modules --- {submodules => modules}/project_cleanup/README.md | 0 {submodules => modules}/project_cleanup/function_source/README.md | 0 {submodules => modules}/project_cleanup/function_source/go.mod | 0 {submodules => modules}/project_cleanup/function_source/go.sum | 0 {submodules => modules}/project_cleanup/function_source/main.go | 0 {submodules => modules}/project_cleanup/main.tf | 0 {submodules => modules}/project_cleanup/outputs.tf | 0 {submodules => modules}/project_cleanup/terraform.tfvars.sample | 0 {submodules => modules}/project_cleanup/variables.tf | 0 9 files changed, 0 insertions(+), 0 deletions(-) rename {submodules => modules}/project_cleanup/README.md (100%) rename {submodules => modules}/project_cleanup/function_source/README.md (100%) rename {submodules => modules}/project_cleanup/function_source/go.mod (100%) rename {submodules => modules}/project_cleanup/function_source/go.sum (100%) rename {submodules => modules}/project_cleanup/function_source/main.go (100%) rename {submodules => modules}/project_cleanup/main.tf (100%) rename {submodules => modules}/project_cleanup/outputs.tf (100%) rename {submodules => modules}/project_cleanup/terraform.tfvars.sample (100%) rename {submodules => modules}/project_cleanup/variables.tf (100%) diff --git a/submodules/project_cleanup/README.md b/modules/project_cleanup/README.md similarity index 100% rename from submodules/project_cleanup/README.md rename to modules/project_cleanup/README.md diff --git a/submodules/project_cleanup/function_source/README.md b/modules/project_cleanup/function_source/README.md similarity index 100% rename from submodules/project_cleanup/function_source/README.md rename to modules/project_cleanup/function_source/README.md diff --git a/submodules/project_cleanup/function_source/go.mod b/modules/project_cleanup/function_source/go.mod similarity index 100% rename from submodules/project_cleanup/function_source/go.mod rename to modules/project_cleanup/function_source/go.mod diff --git a/submodules/project_cleanup/function_source/go.sum b/modules/project_cleanup/function_source/go.sum similarity index 100% rename from submodules/project_cleanup/function_source/go.sum rename to modules/project_cleanup/function_source/go.sum diff --git a/submodules/project_cleanup/function_source/main.go b/modules/project_cleanup/function_source/main.go similarity index 100% rename from submodules/project_cleanup/function_source/main.go rename to modules/project_cleanup/function_source/main.go diff --git a/submodules/project_cleanup/main.tf b/modules/project_cleanup/main.tf similarity index 100% rename from submodules/project_cleanup/main.tf rename to modules/project_cleanup/main.tf diff --git a/submodules/project_cleanup/outputs.tf b/modules/project_cleanup/outputs.tf similarity index 100% rename from submodules/project_cleanup/outputs.tf rename to modules/project_cleanup/outputs.tf diff --git a/submodules/project_cleanup/terraform.tfvars.sample b/modules/project_cleanup/terraform.tfvars.sample similarity index 100% rename from submodules/project_cleanup/terraform.tfvars.sample rename to modules/project_cleanup/terraform.tfvars.sample diff --git a/submodules/project_cleanup/variables.tf b/modules/project_cleanup/variables.tf similarity index 100% rename from submodules/project_cleanup/variables.tf rename to modules/project_cleanup/variables.tf From 871d79c0917b1d8abf72c8f27ebe75d236a5161e Mon Sep 17 00:00:00 2001 From: Jason Berlinsky Date: Mon, 10 Jun 2019 23:55:44 -0400 Subject: [PATCH 14/14] Extract project services and provider configuration to calling module --- modules/project_cleanup/README.md | 12 +++++++++++ modules/project_cleanup/main.tf | 33 +------------------------------ 2 files changed, 13 insertions(+), 32 deletions(-) diff --git a/modules/project_cleanup/README.md b/modules/project_cleanup/README.md index 95534af..21f5ff4 100644 --- a/modules/project_cleanup/README.md +++ b/modules/project_cleanup/README.md @@ -2,8 +2,20 @@ This module schedules a job to clean up GCP projects older than a specified length of time, that match a particular key-value pair. This job runs every 5 minutes via Google Cloud Scheduled Functions. Please see the [utility's readme](./function_source/README.md) for more information as to its operation and configuration. +## Requirements + +### App Engine + Running this module requires an App Engine app in the specified project/region. More information is in the [root readme](../../README.md#app-engine). +### Enabled Services + +The following services must be enabled on the project housing the cleanup function prior to invoking this module: + +- Cloud Functions (`cloudfunctions.googleapis.com`) +- Cloud Scheduler (`cloudscheduler.googleapis.com`) +- Cloud Resource Manager (`cloudresourcemanager.googleapis.com`) + [^]: (autogen_docs_start) diff --git a/modules/project_cleanup/main.tf b/modules/project_cleanup/main.tf index b0efc39..53f6e25 100644 --- a/modules/project_cleanup/main.tf +++ b/modules/project_cleanup/main.tf @@ -14,33 +14,6 @@ * limitations under the License. */ -provider "google-beta" { - version = "~> 2.1" - project = "${var.project_id}" - region = "${var.region}" -} - -resource "google_project_service" "cloudfunctions" { - project = "${var.project_id}" - service = "cloudfunctions.googleapis.com" - disable_dependent_services = true - disable_on_destroy = false -} - -resource "google_project_service" "cloudscheduler" { - project = "${google_project_service.cloudfunctions.project}" - service = "cloudscheduler.googleapis.com" - disable_dependent_services = true - disable_on_destroy = false -} - -resource "google_project_service" "cloudresourcemanager" { - project = "${google_project_service.cloudscheduler.project}" - service = "cloudresourcemanager.googleapis.com" - disable_dependent_services = true - disable_on_destroy = false -} - resource "google_service_account" "project_cleaner_function" { project = "${var.project_id}" account_id = "project-cleaner-function" @@ -54,12 +27,8 @@ resource "google_organization_iam_member" "project_owner" { } module "scheduled_project_cleaner" { - providers = { - google = "google-beta" - } - source = "../../" - project_id = "${google_project_service.cloudresourcemanager.project}" + project_id = "${var.project_id}" job_name = "project-cleaner" job_schedule = "*/5 * * * *" function_entry_point = "CleanUpProjects"